Merge branch 'main' of http://192.168.1.200:3000/Stratawiz/Rapport-Laravel
Specific Folder FTP Upload / ftp-deploy (push) Successful in 1m43s Details

This commit is contained in:
zeeshan 2026-10-05 13:13:29 +05:30
commit 95ed301da3
41 changed files with 3515 additions and 201 deletions

View File

@ -0,0 +1,55 @@
---
description: Rapport form Blade templates — resolution order, patterns, guardrails
globs: resources/views/form/**/*.blade.php
alwaysApply: false
---
# Form views (`resources/views/form`)
## View selection (must match `FormController::deliverform`)
Resolution order for **form** links:
1. `form.{partition}.{form_id}` — Blade at `form/{partition}/{form_id}.blade.php` (controller uses `is_file` on a deploy-specific base path; if checks fail, overrides are skipped).
2. `form.{form_id}` — `form/{form_id}.blade.php` at folder root.
3. `form.common-enrollmentform` if `$formdata->form_type === 'enrollment_form'`.
4. `form.common-planexecution` if `$formdata->form_type === 'plan_vs_execution'`.
5. Else `form.common`.
**List** (`link_type == 'list'`): `form.{partition}.list-{form_id}` → `form.list-{form_id}` → `form.{partition}.list` → `form.list`. History views use similar `View::exists` patterns in the same controller.
If a new Blade’s name/path does not match this cascade, it will not load.
## Default implementation
- Prefer copying or extending **`form.common`**: `@foreach ($questions as $question)` and `@switch ($question->type)` (`hidden`, `text`, `email`, `tel`, `date`, `number`, `textarea`, `dropdown`, `radio`, `radiometrix`, `checkbox`, `file`, `multitextbox`).
- Use **`$question->code`** for `name` / `id` (`id="field-{{ $question->code }}"`). Checkbox names use `[]` where existing templates do.
- Preserve **`$questions`**, **`$form` / `$formdata`**, **`$form_id`**, **`$id`**, and optional **`$mode`**, **`$demo`**, **`$additional`** — do not rename without updating the controller and all templates.
## Layout and assets
- Reuse `@include('layouts.shared/title-meta', ...)`, `layouts.shared/head-css`, `layouts.shared/footer-script`, existing SweetAlert2 paths, `#pageloader`, and Bootstrap card / `account-pages` structure used in sibling blades.
## Submit behaviour
- Standard submit form: `id="report-form"`, `method="POST"`, `action="/form/submit"`, `enctype="multipart/form-data"`, `@csrf`, hidden `form_id`, optional hidden `id`. Client-side submit typically POSTs `FormData` to **`/form/ajaxsubmit`** with SweetAlert2 feedback — match `form.common` unless the page is explicitly non-submit (e.g. list-only).
### Required submit handler + notifications (SweetAlert2)
- For any submit-capable form Blade (`link_type == 'form'`), prefer using the exact `$('#report-form').on('submit', function(event){ ... })` pattern from `form.common`:
- prevent default submit
- show/hide `#pageloader`
- `POST` `new FormData($('#report-form')[0])` to `/form/ajaxsubmit`
- Use **SweetAlert2 (`Swal.fire`)** for all user-facing success/error messages in JS (no `alert()`, no console-only messages).
## Partition / client overrides
- Client-specific templates live under **`form/rapport-{client}/`**. File name must be **`{form_id}.blade.php`** (or the `list-` / `-history` variants the controller checks) so Laravel’s `form.{partition}.{form_id}` resolves.
## Do not
- Add a new stack (SPA framework, new global layout) or new submit URLs without **routes** and **`FormController`** changes.
- Remove approval / read-only behaviour (`is_approval`, file upload vs preview) when copying from `common` unless the task explicitly requires it.
- Refactor unrelated modules or shared layouts “while you’re here”.
When unsure, open **`form.common`** and the closest sibling template (e.g. **`form.common-planexecution`**, **`form.list`**) and mirror their structure.

View File

@ -0,0 +1,153 @@
<?php
namespace App\Exports;
use Maatwebsite\Excel\Concerns\FromArray;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\AfterSheet;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Fill;
class ZenovaReportExport implements FromArray, WithEvents
{
protected $groups;
protected $body;
protected $totals;
public function __construct(array $groups, array $body, array $totals)
{
$this->groups = $groups;
$this->body = $body;
$this->totals = $totals;
}
public function array(): array
{
$groupRow = [];
$labelRow = [];
foreach ($this->groups as $group) {
$first = true;
foreach ($group['columns'] as $column) {
$groupRow[] = $first ? $group['label'] : '';
$labelRow[] = $column['label'];
$first = false;
}
}
$rows = [$groupRow, $labelRow];
foreach ($this->body as $line) {
$rows[] = $line;
}
if (!empty($this->totals)) {
$rows[] = $this->totals;
}
return $rows;
}
public function registerEvents(): array
{
return [
AfterSheet::class => function (AfterSheet $event) {
$sheet = $event->sheet->getDelegate();
$rows = $this->array();
$rowCount = max(1, count($rows));
$colCount = 0;
foreach ($this->groups as $group) {
$colCount += count($group['columns']);
}
if ($colCount < 1) {
return;
}
$lastCol = Coordinate::stringFromColumnIndex($colCount);
$colors = [
'demo' => 'FFF2CC',
'day' => 'DDEBF7',
'yesterday' => 'E2EFDA',
'today' => 'C6EFCE',
'mtd' => 'FCE4D6',
'ytd' => 'D9E2F3',
];
$sheet->getStyle('A1:' . $lastCol . $rowCount)->applyFromArray([
'font' => [
'name' => 'Calibri',
'size' => 11,
],
'borders' => [
'allBorders' => [
'borderStyle' => Border::BORDER_THIN,
'color' => ['rgb' => 'B0B0B0'],
],
],
'alignment' => [
'vertical' => Alignment::VERTICAL_CENTER,
],
]);
$col = 1;
foreach ($this->groups as $group) {
$span = count($group['columns']);
$start = Coordinate::stringFromColumnIndex($col);
$end = Coordinate::stringFromColumnIndex($col + $span - 1);
if ($span > 1) {
$sheet->mergeCells($start . '1:' . $end . '1');
}
$sheet->getStyle($start . '1:' . $end . '2')->applyFromArray([
'font' => [
'bold' => true,
'color' => ['rgb' => '000000'],
],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => $colors[$group['tone']] ?? 'FFFFFF'],
],
'alignment' => [
'horizontal' => Alignment::HORIZONTAL_CENTER,
'vertical' => Alignment::VERTICAL_CENTER,
'wrapText' => true,
],
]);
foreach ($group['columns'] as $offset => $column) {
$letter = Coordinate::stringFromColumnIndex($col + $offset);
$width = $column['type'] === 'text' ? 24 : 16;
$sheet->getColumnDimension($letter)->setWidth($width);
if ($rowCount < 3) {
continue;
}
$dataRange = $letter . '3:' . $letter . $rowCount;
if ($column['type'] === 'text') {
$sheet->getStyle($dataRange)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_LEFT);
} elseif ($column['type'] === 'percent') {
$sheet->getStyle($dataRange)->getNumberFormat()->setFormatCode('0.00"%"');
$sheet->getStyle($dataRange)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
} else {
$sheet->getStyle($dataRange)->getNumberFormat()->setFormatCode('#,##0');
$sheet->getStyle($dataRange)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
}
}
$col += $span;
}
$sheet->getRowDimension(1)->setRowHeight(22);
$sheet->getRowDimension(2)->setRowHeight(36);
$sheet->freezePane('A3');
if (!empty($this->totals)) {
$sheet->getStyle('A' . $rowCount . ':' . $lastCol . $rowCount)->applyFromArray([
'font' => ['bold' => true],
'fill' => [
'fillType' => Fill::FILL_SOLID,
'startColor' => ['rgb' => 'F2F2F2'],
],
]);
}
},
];
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace App\Helpers;
use Illuminate\Support\Facades\Auth;
class ZenovaReportAccess
{
public const ROLE_IDS = [
'1650360764-424692c0-654a-4af2-zenova-64ce55c66088', // HO
'1787808155-9945ee91-7a22-4be6-b184-733610e66ddc', // Admin
];
public static function canView(): bool
{
$user = Auth::user();
if (!$user || ($user->domain ?? '') !== 'zenova') {
return false;
}
return count(array_intersect(self::roleIds($user->roles ?? []), self::ROLE_IDS)) > 0;
}
private static function roleIds($roles): array
{
if (is_string($roles)) {
$decoded = json_decode($roles, true);
$roles = is_array($decoded) ? $decoded : [];
}
if (!is_array($roles)) {
return [];
}
$ids = [];
foreach ($roles as $role) {
if (is_scalar($role) && (string) $role !== '') {
$ids[] = (string) $role;
}
}
return $ids;
}
}

View File

@ -0,0 +1,235 @@
<?php
namespace App\Http\Controllers;
use Carbon\Carbon;
class ActionsheetController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->_service = 'formreminders';
$this->_module = 'action_sheet';
$this->_viewname = 'actionsheet';
$this->_title = 'Action Sheets';
$this->_base_url = '/actionsheet';
}
protected function get_list_fields()
{
return [
'actions' => ['title' => 'Actions', 'buttons' => ['edit', 'delete']],
'title' => ['title' => 'Title', 'type' => 'text'],
'period' => ['title' => 'Period', 'type' => 'text'],
'interval' => ['title' => 'Interval', 'type' => 'text'],
'sort_order' => ['title' => 'Sort Order', 'type' => 'number'],
'show_popup' => ['title' => 'Show Popup', 'type' => 'text'],
'status' => ['title' => 'Status', 'type' => 'select'],
];
}
protected function get_form_fields($type, $values)
{
$roles = $this->getlambda('get', 'ums', 'roles', 'dropdown');
$roles_dd = [];
foreach (($roles->data->items ?? []) as $role) {
$roles_dd[$role->id] = $role->name;
}
$metrics = $this->getlambda('get', 'metric', 'metric', 'get_all');
$metrics_dd = [];
foreach (($metrics->data->items ?? []) as $metric) {
$name = $metric->name ?? ($metric->title ?? $metric->id);
$metrics_dd[$metric->id] = $name;
}
return [
'id' => ['title' => '', 'type' => 'hidden'],
'title' => ['title' => 'Title', 'type' => 'text', 'required' => true],
'sort_order' => ['title' => 'Sort Order', 'type' => 'number', 'required' => false],
'entity_id' => ['title' => 'Entity ID', 'type' => 'text', 'required' => false],
'status' => ['title' => 'Status', 'type' => 'select', 'required' => true, 'values' => ['active' => 'Active', 'disabled' => 'Disabled'], 'default' => 'active'],
'interval_start' => ['title' => 'Interval Start', 'type' => 'date', 'required' => true],
'interval_end' => ['title' => 'Interval End', 'type' => 'date', 'required' => true],
'period' => ['title' => 'Period', 'type' => 'select', 'required' => true, 'values' => [
'daily' => 'Daily',
'weekly' => 'Weekly',
'monthly' => 'Monthly',
'yearly' => 'Yearly',
'custom' => 'Custom',
'overall' => 'Overall',
], 'default' => 'monthly'],
'custom_start_date' => ['title' => 'Custom Start Date', 'type' => 'date', 'required' => false],
'custom_end_date' => ['title' => 'Custom End Date', 'type' => 'date', 'required' => false],
'visible_roles' => ['title' => 'Visible Roles', 'type' => 'multiselect', 'values' => $roles_dd, 'required' => true],
'reporting_roles' => ['title' => 'Reporting Roles', 'type' => 'multiselect', 'values' => $roles_dd, 'required' => true],
'model_name' => ['title' => 'Model', 'type' => 'select', 'required' => true, 'values' => [
'metric_aggregation' => 'Metric Aggregation',
'rapport_roles_leaderboard' => 'Roles Leaderboard',
'rapport_metric_views' => 'Metric Views',
'rapport_campaign_view' => 'Campaign View',
'rapport_milestones' => 'Milestones',
], 'default' => 'metric_aggregation'],
'aggregation_type' => ['title' => 'Aggregation Type', 'type' => 'select', 'required' => true, 'values' => ['sum' => 'Sum', 'count' => 'Count', 'avg' => 'Average'], 'default' => 'sum'],
'show_aggregation_data' => ['title' => 'Show Aggregation Data', 'type' => 'select', 'required' => true, 'values' => ['login' => 'Login hierarchy', 'metric' => 'All reporting roles'], 'default' => 'login'],
'leaderboard_chart' => ['title' => 'Leaderboard Chart', 'type' => 'text', 'required' => false],
'leaderboard_type' => ['title' => 'Leaderboard Type', 'type' => 'text', 'required' => false],
'metrics' => ['title' => 'Metrics', 'type' => 'sheet_metrics', 'required' => false, 'fullwidth' => true, 'metrics' => $metrics_dd],
'html_format' => ['title' => 'HTML Format', 'type' => 'textarea', 'required' => false, 'fullwidth' => true],
'show_popup' => ['title' => 'Show Popup', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'no'],
'repeat_popup' => ['title' => 'Repeat Popup', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'no'],
'repeat_value' => ['title' => 'Repeat Value', 'type' => 'number', 'required' => false, 'default' => 1],
'repeat_period' => ['title' => 'Repeat Period', 'type' => 'select', 'required' => false, 'values' => ['hourly' => 'Hourly', 'daily' => 'Daily', 'weekly' => 'Weekly', 'monthly' => 'Monthly'], 'default' => 'daily'],
];
}
protected function _beforeform($type, $values)
{
if ($type !== 'edit' || !is_object($values)) {
return $values;
}
$toArray = function ($value) {
if (!isset($value) || $value === null || $value === '') {
return [];
}
if (is_array($value)) {
return array_values(array_filter($value, function ($item) {
return $item !== '' && $item !== null;
}));
}
if (is_object($value)) {
return array_values((array) $value);
}
return array_values(array_filter(explode('#', (string) $value), function ($item) {
return $item !== '';
}));
};
$values->repeat_popup = $values->repeat_popup ?? 'no';
$values->repeat_value = $values->repeat_value ?? 1;
$values->repeat_period = $values->repeat_period ?? 'daily';
$values->visible_roles = $toArray($values->visible_roles ?? null);
$values->reporting_roles = $toArray($values->reporting_roles ?? null);
$parts = explode('_', (string) ($values->interval ?? ''), 2);
$values->interval_start = $parts[0] ?? '';
$values->interval_end = $parts[1] ?? '';
$aggregation = json_decode(json_encode($values->aggregation ?? new \stdClass()), true) ?: [];
$values->aggregation_type = $aggregation['aggregation_type'] ?? 'sum';
$values->show_aggregation_data = $aggregation['show_aggregation_data'] ?? 'login';
$values->leaderboard_chart = $aggregation['leaderboard_chart'] ?? '';
$values->leaderboard_type = $aggregation['leaderboard_type'] ?? '';
$metrics = json_decode(json_encode($values->metrics ?? []), true) ?: [];
$values->metrics = array_values(array_map(function ($row) use ($toArray) {
if (!is_array($row)) {
$row = [];
}
$row['metric_ids'] = $toArray($row['metric_ids'] ?? []);
return $row;
}, $metrics));
return $values;
}
protected function _beforesave($type, $request)
{
$data = $request->except('_token');
if ($type === 'edit') {
if (empty($data['id'])) {
$data['id'] = $request->query('id') ?: $request->input('id');
}
} else {
unset($data['id']);
}
$normalizeList = function ($value) {
if (!isset($value) || $value === null || $value === '') {
return [];
}
if (!is_array($value)) {
$value = explode('#', (string) $value);
}
return array_values(array_filter($value, function ($item) {
return $item !== '' && $item !== null;
}));
};
$data['sort_order'] = isset($data['sort_order']) && $data['sort_order'] !== '' ? (int) $data['sort_order'] : 0;
$data['entity_id'] = $data['entity_id'] ?? '';
$data['status'] = $data['status'] ?? 'active';
$data['show_popup'] = $data['show_popup'] ?? 'no';
$data['repeat_popup'] = $data['repeat_popup'] ?? 'no';
$data['repeat_value'] = isset($data['repeat_value']) && $data['repeat_value'] !== '' ? (int) $data['repeat_value'] : 1;
$data['repeat_period'] = $data['repeat_period'] ?? 'daily';
$data['html_format'] = $data['html_format'] ?? '';
$data['interval'] = $this->toIsoDate($data['interval_start'] ?? '') . '_' . $this->toIsoDate($data['interval_end'] ?? '');
$data['custom_start_date'] = $this->toIsoDate($data['custom_start_date'] ?? '');
$data['custom_end_date'] = $this->toIsoDate($data['custom_end_date'] ?? '');
$data['visible_roles'] = $normalizeList($data['visible_roles'] ?? []);
$data['reporting_roles'] = $normalizeList($data['reporting_roles'] ?? []);
$data['aggregation'] = [
'aggregation_type' => $data['aggregation_type'] ?? 'sum',
'show_aggregation_data' => $data['show_aggregation_data'] ?? 'login',
'leaderboard_chart' => $data['leaderboard_chart'] ?? '',
'leaderboard_type' => $data['leaderboard_type'] ?? '',
];
$metrics = [];
foreach (($data['metrics'] ?? []) as $row) {
if (!is_array($row)) {
continue;
}
$id = trim((string) ($row['id'] ?? ''));
$title = trim((string) ($row['title'] ?? ''));
$label = trim((string) ($row['label'] ?? ''));
$metricIds = $normalizeList($row['metric_ids'] ?? []);
if ($id === '' && $title === '' && $label === '' && empty($metricIds)) {
continue;
}
$metrics[] = [
'id' => $id,
'title' => $title,
'label' => $label,
'metric_ids' => $metricIds,
];
}
$data['metrics'] = $metrics;
unset(
$data['interval_start'],
$data['interval_end'],
$data['aggregation_type'],
$data['show_aggregation_data'],
$data['leaderboard_chart'],
$data['leaderboard_type']
);
return $data;
}
protected function toIsoDate($value)
{
$value = trim((string) $value);
if ($value === '') {
return '';
}
foreach (['Y-m-d', 'm-d-Y', 'm/d/Y', 'd-m-Y'] as $format) {
try {
$date = Carbon::createFromFormat($format, $value);
if ($date !== false) {
return $date->format('Y-m-d');
}
} catch (\Exception $e) {
}
}
return $value;
}
}

View File

@ -49,15 +49,15 @@ class BannerController extends Controller
$forms_dd[$form->id] = $form->title;
}
$forms = $this->getlambda('get', 'form', 'survey', 'dropdown');
foreach($forms->data->items as $form){
$forms_dd[$form->id] = $form->title . ' (Survey)';
}
// $forms = $this->getlambda('get', 'form', 'survey', 'dropdown');
// foreach($forms->data->items as $form){
// $forms_dd[$form->id] = $form->title . ' (Survey)';
// }
$quizs = $this->getlambda('get', 'form', 'quiz', 'dropdown');
foreach($quizs->data->items as $form){
$forms_dd[$form->id] = $form->name . ' (Quiz)';
}
// $quizs = $this->getlambda('get', 'form', 'quiz', 'dropdown');
// foreach($quizs->data->items as $form){
// $forms_dd[$form->id] = $form->name . ' (Quiz)';
// }
return [
'title' => ['title' => 'Title', 'type' => 'text', 'required' => true],
@ -74,7 +74,7 @@ class BannerController extends Controller
'status' => ['title' => 'Status', 'type' => 'select', 'required' => true, 'values' => ['active' => 'Active', 'disabled' => 'Disabled']],
'has_cta' => ['title' => 'Has CTA', 'type' => 'yesno', 'required' => true, 'fullwidth' => true],
'target_type' => ['title' => 'Target Type', 'type' => 'select', 'values' => ['internal_action' => 'Internal Action', 'form' => 'Web Form', 'survey' => 'Surveys', 'quiz' => 'Quizs', 'doctorlist' => 'Doctor List', 'external' => 'External Webpage', 'external_with_token' => 'External Webpage with Token'] ],
'target_type' => ['title' => 'Target Type', 'type' => 'select', 'values' => ['internal_action' => 'Internal Action', 'form' => 'Web Form', 'doctorlist' => 'Doctor List', 'external' => 'External Webpage', 'external_with_token' => 'External Webpage with Token'] ],
'form_id' => ['title' => 'Form', 'type' => 'select', 'values' => $forms_dd ],
'external_url' => ['title' => 'External Url / Internal Action', 'type' => 'text'],

View File

@ -358,6 +358,17 @@ class FormController extends Controller
}
}
$generatedFiles = $submission->formdata->genareted_image ?? null;
if (is_object($generatedFiles)) {
$generatedFiles = array_values((array) $generatedFiles);
}
if (is_array($generatedFiles) && count($generatedFiles)) {
$imageFormId = $submission->formdata->form_id ?? $form_id;
$row['generated_image_url'] = collect($generatedFiles)->filter()->map(function ($file) use ($imageFormId) {
return asset('storage/personalized/'.$imageFormId.'/'.$file);
})->implode(', ');
}
if (isset($formdata->workflow) && $formdata->workflow == 'yes') {
$row['workflow_action_date'] = $this->formatWorkflowActionDate($submission);
$row['approval_history'] = $this->formatApprovalHistory($submission);
@ -594,6 +605,9 @@ class FormController extends Controller
$token = DB::table('users')->select("access_token")->where("domain", $domain)->where("permissions", 'like', '%contact_create%')->orderBy('updated_at', 'desc')->skip(0)->take(1)->get();
//print_r(['form_id' => $form_id, 'id' => $id ]); exit;
LambdaRequest::getrequest('form', 'formentity/deletesubmission', ['form_id' => $form_id, 'id' => $id ], $token[0]->access_token);
if ($form_id === InceptNebAllocation::FORM_ID) {
(new InceptNebAllocation())->releaseBySubmission($id);
}
return response()->json(['success' => true, 'message' => 'Successfully Deleted !!!']);
}
@ -1141,6 +1155,7 @@ class FormController extends Controller
'ecme_dr',
'rtm_dr',
'absi_camp',
'h1_cardio_conference',
];
foreach ($contactAttributes as $attr) {
if (isset($post[$attr]) && $post[$attr] !== '') {
@ -5747,6 +5762,9 @@ class FormController extends Controller
$inceptUsageId = null;
$inceptAllocation = null;
if (($formdata->form_id ?? '') === InceptNebAllocation::FORM_ID) {
if (!empty($post['id'])) {
return response()->json(['status' => 'failure', 'message' => 'Editing is not allowed']);
}
$inceptAllocation = new InceptNebAllocation();
try {
$inceptUsageId = $inceptAllocation->reserveFromSubmission($post, $formdata);
@ -7768,6 +7786,8 @@ class FormController extends Controller
Log::debug(print_r($post, true));
Log::debug(print_r($formdata, true));
$this->syncNapMysqlFromApproval($request, $post, $finfo);
return response()->json($post);
}
@ -7812,6 +7832,45 @@ class FormController extends Controller
return response()->json($post);
}
protected function syncNapMysqlFromApproval(Request $request, array $post, $response): void
{
$napFormId = '1750914801-153443b9-94d0-4dcb-b3f3-4fa11588a0a9';
if (($post['form_id'] ?? '') !== $napFormId) {
return;
}
if (!isset($response->status) || $response->status !== 'success') {
return;
}
$napStatus = $response->data->nap_status ?? '';
$submissionId = $post['submission_id'] ?? $post['id'] ?? '';
if ($napStatus === '' || $submissionId === '') {
return;
}
$record = DB::table('neblogistic')->where('submission_id', $submissionId)->first();
if (!$record) {
return;
}
$update = ['status' => $napStatus];
$now = date('Y-m-d H:i:s');
if ($napStatus === 'Rejected') {
$update['rejected_at'] = $now;
$reason = trim((string) ($post['reason'] ?? ''));
if ($reason !== '') {
$update['rejected_reason'] = $reason;
}
} elseif (preg_match('/^L([1-3]) Approved$/', $napStatus, $match)) {
$update['l' . $match[1] . '_approved_at'] = $now;
} elseif ($napStatus === 'Approved') {
$update['approved_at'] = $now;
}
$old = json_decode(json_encode($record), true);
DB::table('neblogistic')->where('submission_id', $submissionId)->update($update);
NeblogisticAuditService::logUpdate($record->id, $old, $update, null, $request);
}
protected function isValidActingApproverUserId($userId): bool
{
if ($userId === null || $userId === '') {
@ -8281,6 +8340,11 @@ class FormController extends Controller
return Excel::download(new InvoicesExport($exceldata), $formdata->title.'.xlsx');
}
if ($form_id === InceptNebAllocation::FORM_ID) {
$inceptService = new InceptNebAllocation();
$data['incept_neb'] = $inceptService->withOrgTotals([], $formdata);
}
if (\View::exists('form.'.$formdata->partition.'.'.$formdata->form_id.'-history')) {
return view('form.'.$formdata->partition.'.'.$formdata->form_id.'-history')->with($data);
} elseif (\View::exists('form.'.$formdata->form_id.'-history')) {
@ -8344,7 +8408,7 @@ class FormController extends Controller
if ($empId === '' && !empty($formdata->user_id)) {
$empId = $service->resolveEmpIdFromUser($formdata->user_id);
}
return ['incept_neb' => $service->summaryForEmployee($empId)];
return ['incept_neb' => $service->withOrgTotals($service->summaryForEmployee($empId), $formdata)];
break;
default:
@ -8933,6 +8997,7 @@ class FormController extends Controller
);
if (isset($response->status) && $response->status === 'success') {
$this->syncNapMysqlFromApproval($request, $post, $response);
$workflowStatus = 'unapproved';
$approvalRecordStatus = $action === 'approve' ? 'approve' : 'reject';
if (!empty($post['entity_id'])) {

View File

@ -70,17 +70,17 @@ class FormactionController extends Controller
$forms_dd[$form->id] = $form->title;
}
$forms = $this->getlambda('get', 'form', 'survey', 'dropdown');
foreach($forms->data->items as $form){
$forms_dd[$form->id] = $form->title . ' (Survey)';
}
// $forms = $this->getlambda('get', 'form', 'survey', 'dropdown');
// foreach($forms->data->items as $form){
// $forms_dd[$form->id] = $form->title . ' (Survey)';
// }
$quizs = $this->getlambda('get', 'form', 'quiz', 'dropdown');
foreach($quizs->data->items as $form){
$forms_dd[$form->id] = $form->name . ' (Quiz)';
}
// $quizs = $this->getlambda('get', 'form', 'quiz', 'dropdown');
// foreach($quizs->data->items as $form){
// $forms_dd[$form->id] = $form->name . ' (Quiz)';
// }
$target_types = ['form' => 'Web Form', 'survey' => 'Surveys', 'quiz' => 'Quizs', 'doctorlist' => 'Doctor List', 'external' => 'External Webpage', 'internal_action' => 'Internal Action', 'external_with_token' => 'External Webpage with Token', ];
$target_types = ['form' => 'Web Form', 'doctorlist' => 'Doctor List', 'external' => 'External Webpage', 'internal_action' => 'Internal Action', 'external_with_token' => 'External Webpage with Token', ];
$permissions = json_decode(auth()->user()->permissions);
if(in_array('rcpa_reports', $permissions)) {
@ -141,6 +141,7 @@ class FormactionController extends Controller
$actions = $this->getlambda('get', 'form', 'formaction', 'list');
//echo '<pre>'; print_r($actions); exit;
// For all actions, replace external form links with the localhost link
$token = Auth::user()->access_token ?? '';
if (isset($actions->data->actions) && is_array($actions->data->actions)) {
foreach ($actions->data->actions as &$action) {
if (isset($action->url) && is_string($action->url)) {
@ -148,6 +149,9 @@ class FormactionController extends Controller
"https://stage.buildrapport.co/",
"https://form.buildrapport.co/"
], "http://127.0.0.1:8000/", $action->url);
if ($this->actionNeedsAuthToken($action)) {
$action->url = $this->withExternalAuthToken($action->url, $token);
}
}
}
unset($action); // break reference
@ -156,6 +160,65 @@ class FormactionController extends Controller
return view('form.list-console')->with(['actions' => $actions->data->actions]);
}
protected function actionNeedsAuthToken($action) {
if (isset($action->target) && $action->target === 'external_with_token') {
return true;
}
$url = isset($action->url) ? $action->url : '';
return is_string($url) && strpos($url, '/tambola') !== false;
}
protected function withExternalAuthToken($url, $token) {
if (!is_string($url) || $url === '' || !is_string($token) || $token === '') {
return $url;
}
if (preg_match('/(?:authorization|token)=TOKEN(?=&|$)/', $url)) {
return str_replace('TOKEN', rawurlencode($token), $url);
}
if (preg_match('/[?&]TOKEN(?=&|$)/', $url)) {
return preg_replace('/([?&])TOKEN(?=&|$)/', '$1authorization=' . rawurlencode($token), $url, 1);
}
$parts = parse_url($url);
if ($parts === false) {
return $url;
}
$query = [];
if (!empty($parts['query'])) {
parse_str($parts['query'], $query);
}
$existing = '';
if (isset($query['authorization']) && is_string($query['authorization']) && $query['authorization'] !== '') {
$existing = $query['authorization'];
} elseif (isset($query['token']) && is_string($query['token']) && $query['token'] !== '') {
$existing = $query['token'];
}
if ($existing !== '') {
return $url;
}
unset($query['authorization'], $query['token']);
$query['authorization'] = $token;
$rebuilt = '';
if (!empty($parts['scheme'])) {
$rebuilt .= $parts['scheme'] . '://';
}
if (!empty($parts['host'])) {
$rebuilt .= $parts['host'];
}
if (!empty($parts['port'])) {
$rebuilt .= ':' . $parts['port'];
}
$rebuilt .= isset($parts['path']) ? $parts['path'] : '';
$rebuilt .= '?' . http_build_query($query);
if (!empty($parts['fragment'])) {
$rebuilt .= '#' . $parts['fragment'];
}
return $rebuilt;
}
public function consoleshow(Request $request) {
$form_id = $request->input('id', 0);
$finfo = LambdaRequest::postrequest('form', 'formentity/getformdefination', ['form_id' => $form_id, 'user_id' => Auth::user()->user_id, 'partition' => 'rapport-'.Auth::user()->domain ]);

View File

@ -107,6 +107,21 @@ class IvareportController extends Controller
private const COLOR_META = ['bg' => 'E7E6E6', 'fg' => '000000', 'class' => 'iva-th-meta'];
private const COLOR_SKU = ['bg' => 'FFC000', 'fg' => '000000', 'class' => 'iva-th-sku'];
private const SESSION_TOKEN_KEY = 'iva_report_token';
private const TOKEN_ACTIONS = [
'mobile',
'mobileplanner',
'mobilekamwise',
'mobiledrbrandwise',
'mobileskuwise',
'export',
];
private ?string $accessToken = null;
private ?object $tokenUser = null;
private const PLANNER_BRAND_COLORS = [
'Materna HMG' => ['bg' => 'E7E6E6', 'fg' => '000000'],
'Ciscure' => ['bg' => 'F8CBAD', 'fg' => '000000'],
@ -129,11 +144,24 @@ class IvareportController extends Controller
public function __construct()
{
$this->middleware('auth');
// Routes are /ivareport/{action} -> action(), so except() cannot key off mobile* methods.
$this->middleware(function ($request, $next) {
$action = (string) $request->route('action', '');
if (in_array($action, self::TOKEN_ACTIONS, true)) {
return $next($request);
}
if (!auth()->check()) {
if ($request->expectsJson()) {
abort(401);
}
return redirect()->guest(route('login'));
}
if ((auth()->user()->domain ?? '') !== self::DOMAIN) {
abort(403);
}
return $next($request);
});
$this->_service = 'reports';
@ -169,8 +197,58 @@ class IvareportController extends Controller
return $this->renderReport($request, 'sku_wise', 'skuwise', 'SKU wise Rate');
}
public function mobile(Request $request)
{
$this->assertIvaToken($request);
$token = $this->accessToken ?? '';
$shared = array_filter([
'authorization' => $token,
'month' => $request->input('month'),
'filter_hier' => $request->input('filter_hier', []),
'selected_brand' => $request->input('selected_brand', []),
], function ($value) {
return $value !== null && $value !== '' && $value !== [];
});
$query = $shared ? '?' . http_build_query($shared) : '';
return view('ivareport.mobile', [
'title' => 'Custom Dashboards',
'reports' => [
['label' => 'Planner Data', 'url' => '/ivareport/mobileplanner' . $query],
['label' => 'KAM wise details', 'url' => '/ivareport/mobilekamwise' . $query],
['label' => 'Dr & Brand Wise details', 'url' => '/ivareport/mobiledrbrandwise' . $query],
['label' => 'SKU wise Rate', 'url' => '/ivareport/mobileskuwise' . $query],
],
]);
}
public function mobileplanner(Request $request)
{
$this->assertIvaToken($request);
return $this->renderReport($request, 'planner_data', 'planner', 'Planner Data', true);
}
public function mobilekamwise(Request $request)
{
$this->assertIvaToken($request);
return $this->renderReport($request, 'kam_wise', 'kamwise', 'KAM wise details', true);
}
public function mobiledrbrandwise(Request $request)
{
$this->assertIvaToken($request);
return $this->renderReport($request, 'dr_and_brandwise', 'drbrandwise', 'Dr & Brand Wise details', true);
}
public function mobileskuwise(Request $request)
{
$this->assertIvaToken($request);
return $this->renderReport($request, 'sku_wise', 'skuwise', 'SKU wise Rate', true);
}
public function export(Request $request)
{
$this->assertWebOrToken($request);
$typeMap = [
'planner' => ['api' => 'planner_data', 'file' => 'planner-data'],
'kamwise' => ['api' => 'kam_wise', 'file' => 'kam-wise-details'],
@ -209,7 +287,7 @@ class IvareportController extends Controller
return Excel::download(new IvaStyledExport($export), $typeMap[$type]['file'] . '-' . $monthSlug . '.xlsx');
}
private function renderReport(Request $request, string $reportType, string $view, string $title)
private function renderReport(Request $request, string $reportType, string $view, string $title, bool $isMobile = false)
{
$filters = $this->resolveFilters($request, $reportType);
$payload = $this->fetchReport(
@ -218,17 +296,23 @@ class IvareportController extends Controller
$filters['selected_brands'],
$filters['hierarchy_ids']
);
$token = $isMobile ? ($this->accessToken ?? '') : null;
$filterQuery = $this->filterQuery($filters, $token);
$viewData = [
'title' => $title,
'activeTab' => $view,
'isMobile' => $isMobile,
'token' => $token ?? '',
'formAction' => $isMobile ? $this->mobileReportPath($view) : '',
'mobileBackUrl' => $isMobile ? '/ivareport/mobile' . ($filterQuery ? '?' . $filterQuery : '') : '',
'months' => $this->availableMonths(),
'selectedMonth' => $filters['month'],
'filter_hier' => $this->loadHierarchies(),
'filter_hier' => $this->loadHierarchies($isMobile ? $this->tokenUser : null),
'selectedHier' => $filters['hierarchy_ids'],
'brandOptions' => $this->brandOptions($reportType),
'selectedBrands' => $filters['selected_brands'],
'filterQuery' => $this->filterQuery($filters),
'filterQuery' => $filterQuery,
'error' => $payload['error'],
'rows' => [],
'ivaFmt' => function ($val, $isPct = false) {
@ -307,20 +391,119 @@ class IvareportController extends Controller
return self::KAM_BRANDS;
}
private function filterQuery(array $filters): string
private function filterQuery(array $filters, ?string $token = null): string
{
return http_build_query([
$query = [
'month' => $filters['month'],
'filter_hier' => $filters['hierarchy_ids'],
'selected_brand' => $filters['selected_brands'],
]);
];
if ($token) {
$query['authorization'] = $token;
}
return http_build_query($query);
}
private function mobileReportPath(string $view): string
{
return '/ivareport/mobile' . $view;
}
private function assertWebOrToken(Request $request): void
{
$user = auth()->user();
if ($user) {
if (($user->domain ?? '') !== self::DOMAIN) {
abort(403);
}
return;
}
$this->assertIvaToken($request);
}
private function assertIvaToken(Request $request): void
{
$token = $this->resolveAccessToken($request);
if ($token === null) {
abort(401, 'Access token is required');
}
$user = $this->getUserFromToken($token);
if (!$user || ($user->domain ?? '') !== self::DOMAIN) {
abort(403);
}
$this->accessToken = $token;
$this->tokenUser = $user;
}
private function resolveAccessToken(Request $request): ?string
{
$candidates = [
$request->query('authorization'),
$request->query('token'),
$request->header('Authorization'),
];
$token = '';
foreach ($candidates as $candidate) {
$candidate = trim((string) ($candidate ?? ''));
if (stripos($candidate, 'Bearer ') === 0) {
$candidate = trim(substr($candidate, 7));
}
if ($candidate !== '') {
$token = $candidate;
break;
}
}
if ($token === '' && $request->session()->has(self::SESSION_TOKEN_KEY)) {
$token = (string) $request->session()->get(self::SESSION_TOKEN_KEY);
}
if ($token !== '') {
$request->session()->put(self::SESSION_TOKEN_KEY, $token);
}
return $token !== '' ? $token : null;
}
private function getUserFromToken(string $token): ?object
{
$parts = explode('.', $token);
if (count($parts) < 2) {
return null;
}
$payload = json_decode(base64_decode(strtr($parts[1], '-_', '+/')));
if (!is_object($payload)) {
return null;
}
$user = $payload->user ?? null;
if (!is_object($user)) {
return null;
}
if (!isset($user->permissions) && isset($payload->permissions)) {
$user->permissions = $payload->permissions;
}
return $user;
}
private function apiGet(string $service, string $url, array $data, int $timeout = 30)
{
if ($this->accessToken) {
return LambdaRequest::getrequest($service, $url, $data, $this->accessToken, $timeout);
}
return LambdaRequest::signedrequest('get', $service, $url, $data, $timeout);
}
private function fetchReport(string $reportType, string $month, array $brands, array $hierIds): array
{
try {
$response = LambdaRequest::signedrequest(
'get',
$response = $this->apiGet(
'reports',
'iva_reports/brand_reports',
[
@ -359,17 +542,22 @@ class IvareportController extends Controller
return ['report' => $report, 'metric_config' => $metricConfig, 'error' => null];
}
private function loadHierarchies(): array
private function loadHierarchies(?object $user = null): array
{
$permissions = json_decode(auth()->user()->permissions ?? '[]');
$user = $user ?? auth()->user();
$permissions = $user->permissions ?? '[]';
if (is_string($permissions)) {
$permissions = json_decode($permissions, true);
} elseif (is_object($permissions)) {
$permissions = json_decode(json_encode($permissions), true);
}
if (!is_array($permissions)) {
$permissions = [];
}
$hierarchyId = in_array('all_data_access', $permissions, true) ? 'root' : [];
try {
$response = LambdaRequest::signedrequest(
'get',
$response = $this->apiGet(
'ums',
'hierarchy/hierarchy_below_me',
[

View File

@ -119,6 +119,7 @@ class LeaderboardController extends Controller
}
$teams_dd = $this->teamDropdownOptions();
$userattr_dd = $this->getUserAttributeOptions();
$managerLevels = [
'l1manager' => 'L1 Manager',
@ -135,6 +136,7 @@ class LeaderboardController extends Controller
'validity_end' => ['title' => 'Validity end', 'type' => 'date', 'required' => true],
'leaderboard_roles_type' => ['title' => 'Leaderboard Type', 'type' => 'select', 'required' => true, 'values' => ['roles' => 'Roles', 'team' => 'Team']],
'leaderboard_team_ids' => ['title' => 'Teams', 'type' => 'multiselect', 'values' => $teams_dd, 'fullwidth' => true, 'required' => true, 'showIf' => ['leaderboard_roles_type' => 'team']],
'view_mode' => ['title' => 'Team View Mode', 'type' => 'select', 'required' => true, 'values' => ['versus' => 'Versus', 'podium' => 'Podium'], 'default' => 'versus', 'showIf' => ['leaderboard_roles_type' => 'team']],
'visible_roles' => ['title' => 'Visible Roles', 'type' => 'multiselect', 'values' => $roles_dd, 'required' => true],
'reporting_roles' => ['title' => 'Reporting Roles', 'type' => 'multiselect', 'values' => $roles_dd, 'required' => true],
'rank_roles' => ['title' => 'Rank Roles', 'type' => 'rank_roles', 'roles' => $roles_dd, 'required' => true, 'fullwidth' => true, 'showIf' => ['leaderboard_roles_type' => 'roles']],
@ -168,7 +170,13 @@ class LeaderboardController extends Controller
]],
'show_positions' => ['title' => 'Show Positions', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No']],
'show_positions_type' => ['title' => 'Show Positions Type', 'type' => 'select', 'required' => true, 'values' => ['all' => 'All', 'loggedin' => 'Logged In']],
'manager_details' => ['title' => 'Manager Details', 'type' => 'multiselect', 'values' => $managerLevels],
'show_units_export' => ['title' => 'Show Units Export', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'yes'],
'show_user_attributes' => ['title' => 'Show User Attributes', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'no'],
'user_attributes' => ['title' => 'Include User Attributes', 'type' => 'multiselect', 'values' => $userattr_dd, 'default' => []],
'show_normal_metrics' => ['title' => 'Show Normal Metrics', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'yes'],
'show_combined_metrics' => ['title' => 'Show Combined Metrics', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'yes'],
'manager_details' => ['title' => 'Manager Details', 'type' => 'multiselect', 'values' => $managerLevels, 'default' => ['l1manager']],
'total_units_name' => ['title' => 'Total Units Name', 'type' => 'text', 'required' => false, 'default' => ''],
'bg_color' => ['title' => 'Background Color', 'type' => 'color'],
'bg_image' => ['title' => 'Background Image', 'type' => 'image'],
'icon' => ['title' => 'Icon', 'type' => 'image'],
@ -246,12 +254,25 @@ class LeaderboardController extends Controller
if (!isset($data['leaderboard_roles_type']) || !in_array($data['leaderboard_roles_type'], ['team', 'roles'], true)) {
$data['leaderboard_roles_type'] = 'roles';
}
if ($data['leaderboard_roles_type'] !== 'team') {
if ($data['leaderboard_roles_type'] === 'team') {
if (!isset($data['view_mode']) || !in_array($data['view_mode'], ['versus', 'podium'], true)) {
$data['view_mode'] = 'versus';
}
} else {
$data['leaderboard_team_ids'] = [];
$data['view_mode'] = '';
}
if (empty($data['manager_details'])) {
$data['manager_details'] = ['l1manager'];
}
foreach (['show_units_export', 'show_normal_metrics', 'show_combined_metrics'] as $flag) {
if (!isset($data[$flag]) || $data[$flag] === '') {
$data[$flag] = 'yes';
}
}
$data['total_units_name'] = trim((string) ($data['total_units_name'] ?? ''));
$data['show_user_attributes'] = ($data['show_user_attributes'] ?? 'no') === 'yes' ? 'yes' : 'no';
$data['user_attributes'] = $this->normalizeAttributeCodes($data['user_attributes'] ?? []);
if (!isset($data['bg_color']) || is_array($data['bg_color'])) {
$data['bg_color'] = '';
}
@ -297,7 +318,14 @@ class LeaderboardController extends Controller
'intervals' => [],
'leaderboard_roles_type' => 'roles',
'leaderboard_team_ids' => [],
'view_mode' => 'versus',
'manager_details' => ['l1manager'],
'show_units_export' => 'yes',
'show_user_attributes' => 'no',
'user_attributes' => [],
'show_normal_metrics' => 'yes',
'show_combined_metrics' => 'yes',
'total_units_name' => '',
'bg_color' => '',
'bg_image' => '',
'icon' => '',
@ -314,9 +342,22 @@ class LeaderboardController extends Controller
if (!isset($values->leaderboard_roles_type) || !in_array($values->leaderboard_roles_type, ['team', 'roles'], true)) {
$values->leaderboard_roles_type = 'roles';
}
if (!isset($values->view_mode) || !in_array($values->view_mode, ['versus', 'podium'], true)) {
$values->view_mode = 'versus';
}
if (empty($values->manager_details)) {
$values->manager_details = ['l1manager'];
}
foreach (['show_units_export', 'show_normal_metrics', 'show_combined_metrics'] as $flag) {
if (!isset($values->{$flag}) || $values->{$flag} === '') {
$values->{$flag} = 'yes';
}
}
if (!isset($values->total_units_name) || $values->total_units_name === null) {
$values->total_units_name = '';
}
$values->show_user_attributes = (($values->show_user_attributes ?? 'no') === 'yes') ? 'yes' : 'no';
$values->user_attributes = $this->normalizeAttributeCodes($values->user_attributes ?? []);
if (!isset($values->bg_color)) {
$values->bg_color = '';
}
@ -388,21 +429,33 @@ class LeaderboardController extends Controller
->withError($data['error']);
}
$headers = ['Rank', 'User', 'Employee Code', 'Designation', 'Position', 'Score'];
if (!empty($data['is_team'])) {
$headers = ['Rank', 'Team', 'Score'];
} else {
$headers = ['Rank', 'User', 'Employee Code', 'Designation', 'Position', 'Score'];
}
foreach ($data['metrics'] as $metric) {
$headers[] = $metric['name'];
}
$exceldata = [$headers];
foreach ($data['users'] as $index => $user) {
$row = [
$user['rank'] ?? ($index + 1),
$user['name'] ?? '',
$user['employee_code'] ?? '',
$user['designation'] ?? '',
$user['position_name'] ?? '',
$user['score'] ?? 0,
];
if (!empty($data['is_team'])) {
$row = [
$user['rank'] ?? ($index + 1),
$user['name'] ?? '',
$user['score'] ?? 0,
];
} else {
$row = [
$user['rank'] ?? ($index + 1),
$user['name'] ?? '',
$user['employee_code'] ?? '',
$user['designation'] ?? '',
$user['position_name'] ?? '',
$user['score'] ?? 0,
];
}
foreach ($data['metrics'] as $metric) {
$row[] = $user['metric_values'][$metric['id']] ?? '';
}
@ -430,6 +483,7 @@ class LeaderboardController extends Controller
'metrics' => [],
'pills' => [],
'selected_role' => $roleId,
'is_team' => false,
];
if ($id === '') {
@ -455,6 +509,11 @@ class LeaderboardController extends Controller
$groups = array_values((array) $groups);
}
$teamRows = $this->teamRowsFromGroups($groups);
if ($this->isTeamLeaderboardView($view->team_view ?? false) || count($teamRows)) {
return $this->teamLeaderboardViewData($view, $teamRows, $request);
}
$selected = null;
foreach ($groups as $group) {
$group = is_object($group) ? $group : (object) $group;
@ -486,6 +545,7 @@ class LeaderboardController extends Controller
'metrics' => [],
'pills' => $pills,
'selected_role' => $roleId,
'is_team' => false,
];
}
@ -553,6 +613,87 @@ class LeaderboardController extends Controller
'metrics' => $metrics,
'pills' => $pills,
'selected_role' => $roleId,
'is_team' => false,
];
}
protected function isTeamLeaderboardView($flag): bool
{
return $flag === true || $flag === 1 || $flag === '1' || $flag === 'true' || $flag === 'yes';
}
protected function teamRowsFromGroups(array $groups): array
{
foreach ($groups as $group) {
$group = is_object($group) ? $group : (object) $group;
$rows = $group->teams ?? null;
if (is_object($rows)) {
$rows = array_values((array) $rows);
}
if (is_array($rows) && count($rows)) {
return $rows;
}
}
return [];
}
protected function teamLeaderboardViewData($view, array $teamRows, Request $request): array
{
$periods = $this->uniquePeriodOptions($view->periods ?? []);
$selectedPeriod = (string) ($request->query('period') ?: ($view->interval ?? ''));
if ($selectedPeriod === '') {
$selectedPeriod = 'all';
}
$metrics = [];
$users = [];
foreach ($teamRows as $index => $row) {
$row = is_object($row) ? $row : (object) $row;
$team = is_object($row->team ?? null) ? $row->team : (object) ($row->team ?? []);
$metricValues = [];
$rowMetrics = $row->metrics ?? [];
if (is_object($rowMetrics)) {
$rowMetrics = array_values((array) $rowMetrics);
}
if (is_array($rowMetrics)) {
foreach ($rowMetrics as $metric) {
$metric = is_object($metric) ? $metric : (object) $metric;
$metricId = $metric->metric ?? ($metric->title ?? ('m' . count($metrics)));
$metricName = $metric->name ?? ($metric->display ?? ($metric->title ?? $metricId));
if ($index === 0) {
$metrics[] = ['id' => $metricId, 'name' => $metricName];
}
$metricValues[$metricId] = $metric->value ?? 0;
}
}
$name = trim((string) ($team->name ?? ''));
$users[] = [
'rank' => $row->rank ?? ($index + 1),
'name' => $name !== '' ? $name : 'Team',
'avatar' => $team->icon ?? '',
'employee_code' => '',
'designation' => '',
'position_name' => '',
'score' => $row->score ?? 0,
'metric_values' => $metricValues,
];
}
$periodLabel = trim(($view->firstDay ?? '') . (($view->firstDay ?? '') && ($view->lastDay ?? '') ? ' – ' : '') . ($view->lastDay ?? ''));
return [
'error' => '',
'title' => $view->name ?? 'Leaderboard',
'subtitle' => 'Teams',
'period_label' => $periodLabel,
'periods' => $periods,
'selected_period' => $selectedPeriod,
'users' => $users,
'metrics' => $metrics,
'pills' => [],
'selected_role' => '',
'is_team' => true,
];
}
@ -633,4 +774,46 @@ class LeaderboardController extends Controller
return $pills;
}
protected function getUserAttributeOptions(): array
{
$options = [];
$userAttrs = $this->getlambda('get', 'ums', 'userattribute', 'get_all');
foreach (($userAttrs->data->items ?? []) as $attr) {
if (($attr->status ?? 'active') === 'disabled') {
continue;
}
$code = $attr->code ?? '';
if ($code === '') {
continue;
}
$options[$code] = $attr->attribute ?? $code;
}
return $options;
}
protected function normalizeAttributeCodes($raw): array
{
if (is_string($raw) && $raw !== '') {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : explode('#', $raw);
}
if (!is_array($raw)) {
return [];
}
$codes = [];
foreach ($raw as $code) {
if (is_object($code)) {
$code = $code->code ?? '';
}
$code = trim((string) $code);
if ($code !== '') {
$codes[] = $code;
}
}
return array_values(array_unique($codes));
}
}

View File

@ -67,15 +67,15 @@ class MagmediaController extends Controller
$forms_dd[$form->id] = $form->title;
}
$forms = $this->getlambda('get', 'form', 'survey', 'dropdown');
foreach($forms->data->items as $form){
$forms_dd[$form->id] = $form->title . ' (Survey)';
}
// $forms = $this->getlambda('get', 'form', 'survey', 'dropdown');
// foreach($forms->data->items as $form){
// $forms_dd[$form->id] = $form->title . ' (Survey)';
// }
$quizs = $this->getlambda('get', 'form', 'quiz', 'dropdown');
foreach($quizs->data->items as $form){
$forms_dd[$form->id] = $form->name . ' (Quiz)';
}
// $quizs = $this->getlambda('get', 'form', 'quiz', 'dropdown');
// foreach($quizs->data->items as $form){
// $forms_dd[$form->id] = $form->name . ' (Quiz)';
// }
$metrics = $this->getlambda('get', 'metric', 'metric', 'get_all');
$metric_dd = ['NA' => 'Tracking Not Needed'];
foreach($metrics->data->items as $metric){
@ -85,7 +85,7 @@ class MagmediaController extends Controller
'name' => ['title' => 'Media Name', 'type' => 'text', 'required' => true],
'points' => ['title' => 'Points', 'type' => 'text', 'required' => true],
'description' => ['title' => 'Media Description', 'type' => 'textarea', 'required' => true, 'fullwidth' => true],
'media_type' => ['title' => 'Media Types', 'type' => 'select', 'required' => true, 'values' => ['image' => 'Image', 'video' => 'Video', 'pdf' => 'PDF', 'audio' => 'Audio', 'gallery' => 'Gallery', 'form' => 'Form', 'quiz' => 'Quiz', 'survey' => 'Survey', 'external' => 'External Url', 'scorm' => 'Scorm'] ],
'media_type' => ['title' => 'Media Types', 'type' => 'select', 'required' => true, 'values' => ['image' => 'Image', 'video' => 'Video', 'pdf' => 'PDF', 'audio' => 'Audio', 'gallery' => 'Gallery', 'form' => 'Form', 'external' => 'External Url', 'scorm' => 'Scorm'] ],
'status' => ['title' => 'Status', 'type' => 'select', 'required' => true, 'values' => ['active' => 'Active', 'disabled' => 'Disabled'] ],
'form_id' => ['title' => 'Form', 'type' => 'select', 'values' => $forms_dd ],
//'media_thumb' => ['title' => 'Media Thumb', 'type' => 'image', 'required' => true],

View File

@ -82,6 +82,7 @@ class MetriccampaignController extends Controller
$userAttrs = $this->getlambda('get', 'ums', 'userattribute', 'get_all');
$attrs_dd = [];
$userattr_dd = [];
$attrs_meta = [];
foreach (($userAttrs->data->items ?? []) as $attr) {
if (($attr->status ?? 'active') === 'disabled') {
@ -101,6 +102,7 @@ class MetriccampaignController extends Controller
$options[$optValue] = (string) ($opt['display'] ?? $optValue);
}
$attrs_dd[$code] = ($attr->attribute ?? $code) . ' (' . $code . ')';
$userattr_dd[$code] = $attr->attribute ?? $code;
$attrs_meta[$code] = [
'code' => $code,
'attribute' => $attr->attribute ?? $code,
@ -232,6 +234,13 @@ class MetriccampaignController extends Controller
'status' => ['title' => 'Status', 'type' => 'select', 'required' => true, 'values' => ['active' => 'Active', 'disabled' => 'Disabled']],
'show_positions' => ['title' => 'Show Positions', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No']],
'show_positions_type' => ['title' => 'Show Positions Type', 'type' => 'select', 'required' => true, 'values' => ['all' => 'All', 'loggedin' => 'Logged In']],
'show_units_export' => ['title' => 'Show Units Export', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'yes'],
'show_user_attributes' => ['title' => 'Show User Attributes', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'no'],
'user_attributes' => ['title' => 'Include User Attributes', 'type' => 'multiselect', 'values' => $userattr_dd, 'default' => []],
'show_normal_metrics' => ['title' => 'Show Normal Metrics', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'yes'],
'show_combined_metrics' => ['title' => 'Show Combined Metrics', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'yes'],
'manager_details' => ['title' => 'Manager Details', 'type' => 'multiselect', 'values' => ['l1manager' => 'L1 Manager', 'l2manager' => 'L2 Manager', 'l3manager' => 'L3 Manager', 'l4manager' => 'L4 Manager'], 'default' => ['l1manager']],
'total_units_name' => ['title' => 'Total Units Name', 'type' => 'text', 'required' => false, 'default' => ''],
'highlight_top' => [
'title' => 'Highlight Top',
'type' => 'text',
@ -290,18 +299,29 @@ class MetriccampaignController extends Controller
'showTeamRank' => 'yes',
'showManagerOverallRank' => 'yes',
'showManagerTeamRank' => 'yes',
'show_units_export' => 'yes',
'show_normal_metrics' => 'yes',
'show_combined_metrics' => 'yes',
];
foreach ($yesNoDefaults as $field => $default) {
if (!isset($values->{$field}) || $values->{$field} === '' || $values->{$field} === null) {
$values->{$field} = $default;
}
}
$values->show_user_attributes = (($values->show_user_attributes ?? 'no') === 'yes') ? 'yes' : 'no';
$values->user_attributes = $this->normalizeAttributeCodes($values->user_attributes ?? []);
if (!isset($values->highlight_top) || $values->highlight_top === null) {
$values->highlight_top = '';
}
if (!isset($values->highlight_color) || $values->highlight_color === null) {
$values->highlight_color = '';
}
if (!isset($values->manager_details) || !is_array($values->manager_details) || $values->manager_details === []) {
$values->manager_details = ['l1manager'];
}
if (!isset($values->total_units_name) || $values->total_units_name === null) {
$values->total_units_name = '';
}
if (!isset($values->sort_order) || $values->sort_order === null || $values->sort_order === '') {
$values->sort_order = 0;
}
@ -315,6 +335,13 @@ class MetriccampaignController extends Controller
$values->showManagerTeamRank = 'yes';
$values->highlight_top = '';
$values->highlight_color = '';
$values->show_units_export = 'yes';
$values->show_user_attributes = 'no';
$values->user_attributes = [];
$values->show_normal_metrics = 'yes';
$values->show_combined_metrics = 'yes';
$values->manager_details = ['l1manager'];
$values->total_units_name = '';
}
return $values;
}
@ -637,6 +664,18 @@ class MetriccampaignController extends Controller
? (int) $data['sort_order']
: 0;
if (!isset($data['manager_details']) || !is_array($data['manager_details']) || $data['manager_details'] === []) {
$data['manager_details'] = ['l1manager'];
}
foreach (['show_units_export', 'show_normal_metrics', 'show_combined_metrics'] as $flag) {
if (!isset($data[$flag]) || $data[$flag] === '') {
$data[$flag] = 'yes';
}
}
$data['total_units_name'] = trim((string) ($data['total_units_name'] ?? ''));
$data['show_user_attributes'] = ($data['show_user_attributes'] ?? 'no') === 'yes' ? 'yes' : 'no';
$data['user_attributes'] = $this->normalizeAttributeCodes($data['user_attributes'] ?? []);
unset(
$data['metric'],
$data['display_unit'],
@ -654,5 +693,29 @@ class MetriccampaignController extends Controller
);
return $data;
}
protected function normalizeAttributeCodes($raw): array
{
if (is_string($raw) && $raw !== '') {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : explode('#', $raw);
}
if (!is_array($raw)) {
return [];
}
$codes = [];
foreach ($raw as $code) {
if (is_object($code)) {
$code = $code->code ?? '';
}
$code = trim((string) $code);
if ($code !== '') {
$codes[] = $code;
}
}
return array_values(array_unique($codes));
}
}

View File

@ -196,23 +196,28 @@ class MetricviewController extends Controller
'agg_type' => ['title' => 'Aggregation Type', 'type' => 'select', 'required' => true, 'values' => ['individual' => 'Individual', 'team' => 'Team'] ],
'graphical_views' => ['title' => 'Graphical views', 'type' => 'multiselect', 'values' => ['progress' => 'Progress', 'percentile' => 'Percentile', 'pie' => 'Pie chart', 'bar' => 'Bar chart', 'line' => 'Line chart'] ],
'details_views' => ['title' => 'Details views', 'type' => 'multiselect', 'required' => true, 'values' => ['tabular' => 'Tabular', 'contacts' => 'Doctor View', 'incremental' => 'Incremental View'] ],
'value_views' => ['title' => 'Value views', 'type' => 'multiselect', 'values' => ['value' => 'Value'] ],
'details_views' => ['title' => 'Details views', 'type' => 'multiselect', 'values' => ['tabular' => 'Tabular', 'contacts' => 'Doctor View', 'incremental' => 'Incremental View'] ],
'trend_views' => ['title' => 'Trend views', 'type' => 'multiselect', 'values' => ['bar' => 'Bar chart', 'line' => 'Line chart', 'tabular' => 'Tabular'] ],
'leaderboard_views' => ['title' => 'Leaderboard views', 'type' => 'multiselect', 'values' => ['leaderboard' => 'Leaderboard View'] ],
'leaderboard_views' => ['title' => 'My Team views', 'type' => 'multiselect', 'values' => ['leaderboard' => 'My Team View'] ],
'scoreboard_views' => ['title' => 'Leaderboard views', 'type' => 'multiselect', 'values' => ['leaderboard' => 'Leaderboard View'] ],
'contacts_views' => ['title' => 'Doctor views', 'type' => 'multiselect', 'values' => ['contacts' => 'Doctor View'] ],
'doctor_view_name' => ['title' => 'Enter Doctor View Name', 'type' => 'text', 'required' => false ],
'intervals' => ['title' => 'Intervals', 'type' => 'multiselect', 'required' => true, 'values' => ['yearlycy' => 'Calender Year', 'yearlyfy' => 'Financial Year', 'monthly' => 'Monthly', 'weekly' => 'Weekly', 'daily' => 'Daily'] ],
'default_interval' => ['title' => 'Default Interval', 'type' => 'select', 'required' => true, 'values' => ['month-0' => 'This Month','month-1' => 'Last Month','month-3' => 'Last 3 Months','month-6' => 'Last 6 Months','month-12' => 'Last 12 Months','day-0' => 'Today','day-1' => 'Yesterday','day-7' => 'Last 7 Days','day-30' => 'Last 30 Days','week-0' => 'This week','week-1' => 'Last week','week-2' => '-2 week','week-3' => '-3 week','week-4' => '-4 week','yearfy-0' => 'This Financial Year','yearfy-1' => 'Last Financial Year','yearcy-0' => 'This Calender Year','yearcy-1' => 'Last Calender Year'] ],
'default_tab' => ['title' => 'Default Tab', 'type' => 'select', 'required' => false, 'values' => ['value' => 'Value', 'trend' => 'Trend', 'details' => 'Detail', 'leaderboard' => 'My Team', 'scoreboard' => 'Leaderboard', 'contacts' => 'Doctor List', 'incremental' => 'Incremental'] ],
'widget_sort_order' => ['title' => 'Sort Order', 'type' => 'number', 'required' => true],
'doctor_type' => ['title' => 'Doctor Type', 'type' => 'text', 'required' => false],
'combined_metrics' => ['title' => 'Combined Metrics (if any)', 'type' => 'combined_metrics', 'fullwidth' => true ],
'report_roles' => ['title' => 'Data Report from Roles', 'type' => 'multiselect', 'values' => $roles_dd, 'required' => true],
'team_leaderboard_widget_roles' => ['title' => 'Team Leaderboard Widget Roles', 'type' => 'multiselect', 'values' => $roles_dd, 'required' => false],
'status' => ['title' => 'Status', 'type' => 'select', 'required' => true, 'values' => ['active' => 'Active', 'disabled' => 'Disabled'] ],
'show_positions' => ['title' => 'Show Positions', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'] ],
'stateheadwise_leaderboard' => ['title' => 'Stateheadwise Leaderboard', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'no'],
'show_positions_type' => ['title' => 'Show Positions Type', 'type' => 'select', 'required' => true, 'values' => ['all' => 'All', 'loggedin' => 'Logged In'] ],
'contact_attributes' => ['title' => 'Include Doctor Attributes', 'type' => 'multiselect', 'values' => $coattr_dd],
@ -289,12 +294,14 @@ class MetricviewController extends Controller
unset($data['metric']);
unset($data['display_unit']);
unset($data['weight']);
foreach (['contacts_views', 'graphical_views', 'trend_views', 'leaderboard_views', 'contact_attributes', 'user_attributes'] as $field) {
foreach (['contacts_views', 'graphical_views', 'trend_views', 'leaderboard_views', 'scoreboard_views', 'value_views', 'details_views', 'contact_attributes', 'user_attributes', 'team_leaderboard_widget_roles'] as $field) {
if (!isset($data[$field]) || !is_array($data[$field])) {
$data[$field] = [];
}
}
$data['default_tab'] = trim((string) ($data['default_tab'] ?? ''));
$data['show_user_attributes'] = ($data['show_user_attributes'] ?? 'no') === 'yes' ? 'yes' : 'no';
$data['stateheadwise_leaderboard'] = ($data['stateheadwise_leaderboard'] ?? 'no') === 'yes' ? 'yes' : 'no';
$data['user_attributes'] = $this->normalizeAttributeCodes($data['user_attributes']);
if (array_key_exists('id', $data) && ($data['id'] === null || $data['id'] === '')) {
unset($data['id']);
@ -309,7 +316,11 @@ class MetricviewController extends Controller
'sort_order' => 0,
'widget_sort_order' => 0,
'show_user_attributes' => 'no',
'stateheadwise_leaderboard' => 'no',
'user_attributes' => [],
'team_leaderboard_widget_roles' => [],
'value_views' => ['value'],
'default_tab' => '',
];
if (($values->type ?? '') === 'verses') {
$values->list_chart_type = 'progressbar';
@ -328,6 +339,14 @@ class MetricviewController extends Controller
} else {
$values->report_roles = [];
}
$widgetRoles = $values->team_leaderboard_widget_roles ?? [];
if (is_string($widgetRoles)) {
$values->team_leaderboard_widget_roles = $widgetRoles === '' ? [] : explode('#', $widgetRoles);
} elseif (is_array($widgetRoles)) {
$values->team_leaderboard_widget_roles = array_values($widgetRoles);
} else {
$values->team_leaderboard_widget_roles = [];
}
if(!isset($values->combined_metrics)) {
$values->combined_metrics = '[]';
@ -369,7 +388,17 @@ class MetricviewController extends Controller
$values->show_positions_type = 'all';
}
$values->show_user_attributes = (($values->show_user_attributes ?? 'no') === 'yes') ? 'yes' : 'no';
$values->stateheadwise_leaderboard = (($values->stateheadwise_leaderboard ?? 'no') === 'yes') ? 'yes' : 'no';
$values->user_attributes = $this->normalizeAttributeCodes($values->user_attributes ?? []);
if (!isset($values->value_views)) {
$values->value_views = ['value'];
}
if (!isset($values->scoreboard_views)) {
$values->scoreboard_views = $values->leaderboard_views ?? [];
}
if (!isset($values->default_tab)) {
$values->default_tab = '';
}
if (($values->type ?? '') === 'verses' && (!isset($values->list_chart_type) || $values->list_chart_type === '')) {
$values->list_chart_type = 'progressbar';
}

View File

@ -109,6 +109,8 @@ class MilestoneController extends Controller
//echo '<pre>'; print_r($coattr_dd); exit;
$userattr_dd = $this->getUserAttributeOptions();
$dates_dd = ['L' => 'Last day of month'];
$date = 1;
while($date <= 31) {
@ -127,6 +129,13 @@ class MilestoneController extends Controller
'status' => ['title' => 'Status', 'type' => 'select', 'required' => true, 'values' => ['active' => 'Active', 'disabled' => 'Disabled']],
'show_positions' => ['title' => 'Show Positions', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No']],
'show_positions_type' => ['title' => 'Show Positions Type', 'type' => 'select', 'required' => true, 'values' => ['all' => 'All', 'loggedin' => 'Logged In']],
'show_units_export' => ['title' => 'Show Units Export', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'yes'],
'show_user_attributes' => ['title' => 'Show User Attributes', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'no'],
'user_attributes' => ['title' => 'Include User Attributes', 'type' => 'multiselect', 'values' => $userattr_dd, 'default' => []],
'show_normal_metrics' => ['title' => 'Show Normal Metrics', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'yes'],
'show_combined_metrics' => ['title' => 'Show Combined Metrics', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No'], 'default' => 'yes'],
'manager_details' => ['title' => 'Manager Details', 'type' => 'multiselect', 'values' => ['l1manager' => 'L1 Manager', 'l2manager' => 'L2 Manager', 'l3manager' => 'L3 Manager', 'l4manager' => 'L4 Manager'], 'default' => ['l1manager']],
'total_units_name' => ['title' => 'Total Units Name', 'type' => 'text', 'required' => false, 'default' => ''],
'send_report' => ['title' => 'Send Report', 'type' => 'select', 'required' => true, 'values' => ['yes' => 'Yes', 'no' => 'No']],
'performance_period' => ['title' => 'Performance Period', 'type' => 'select', 'required' => false, 'values' => ['daily' => 'Daily', 'weekly' => 'Weekly', 'monthly' => 'Monthly']],
'days_or_dates' => ['title' => 'Days or Dates', 'type' => 'radio', 'required' => false, 'fullwidth' => true, 'values' => ['days' => 'Days', 'dates' => 'Dates']],
@ -296,6 +305,18 @@ $data['custom_end_date'] = !empty($data['custom_end_date'])
// $data['is_recurring'] = isset($data['is_recurring']) ? (strtolower($data['is_recurring']) === 'yes') : false;
if (!isset($data['manager_details']) || !is_array($data['manager_details']) || $data['manager_details'] === []) {
$data['manager_details'] = ['l1manager'];
}
foreach (['show_units_export', 'show_normal_metrics', 'show_combined_metrics'] as $flag) {
if (!isset($data[$flag]) || $data[$flag] === '') {
$data[$flag] = 'yes';
}
}
$data['show_user_attributes'] = ($data['show_user_attributes'] ?? 'no') === 'yes' ? 'yes' : 'no';
$data['user_attributes'] = $this->normalizeAttributeCodes($data['user_attributes'] ?? []);
$data['total_units_name'] = trim((string) ($data['total_units_name'] ?? ''));
unset($data['interval_start_date']);
unset($data['interval_end_date']);
unset($data['metric']);
@ -327,6 +348,19 @@ $data['custom_end_date'] = !empty($data['custom_end_date'])
protected function _beforeform($type, $values)
{
if ($type == 'edit') {
if (!isset($values->manager_details) || !is_array($values->manager_details) || $values->manager_details === []) {
$values->manager_details = ['l1manager'];
}
foreach (['show_units_export', 'show_normal_metrics', 'show_combined_metrics'] as $flag) {
if (!isset($values->{$flag}) || $values->{$flag} === '') {
$values->{$flag} = 'yes';
}
}
$values->show_user_attributes = (($values->show_user_attributes ?? 'no') === 'yes') ? 'yes' : 'no';
$values->user_attributes = $this->normalizeAttributeCodes($values->user_attributes ?? []);
if (!isset($values->total_units_name) || $values->total_units_name === null) {
$values->total_units_name = '';
}
// split interval
if (!empty($values->interval)) {
@ -525,6 +559,24 @@ $data['custom_end_date'] = !empty($data['custom_end_date'])
$rankRoles = [];
}
$start = $board->validity_start ?? '';
$end = $board->validity_end ?? '';
$detailsUrl = url('/leaderboard/dashboard') . '?' . http_build_query([
'id' => $board->id ?? '',
]);
if ($this->isTeamLeaderboardView($board->team_view ?? false)) {
$items[] = [
'id' => $board->id ?? '',
'title' => $board->name ?? 'Leaderboard',
'interval_label' => trim($start . ($start && $end ? ' – ' : '') . $end),
'details_url' => $detailsUrl,
'podium_users' => $this->podiumUsersFromTeams($rankRoles),
'pills' => [],
];
continue;
}
$pills = [];
foreach ($rankRoles as $index => $rankRole) {
$rankRole = $this->asObject($rankRole);
@ -536,8 +588,7 @@ $data['custom_end_date'] = !empty($data['custom_end_date'])
}
$pills[] = [
'label' => $this->rankRolePillLabel($rankRole->title ?? '', $limit, $role->name ?? ''),
'url' => url('/leaderboard/dashboard') . '?' . http_build_query([
'id' => $board->id ?? '',
'url' => $detailsUrl . '&' . http_build_query([
'role' => $roleId,
]),
'podium_users' => $this->podiumUsersFromRankRole($rankRole),
@ -545,15 +596,11 @@ $data['custom_end_date'] = !empty($data['custom_end_date'])
];
}
$start = $board->validity_start ?? '';
$end = $board->validity_end ?? '';
$items[] = [
'id' => $board->id ?? '',
'title' => $board->name ?? 'Leaderboard',
'interval_label' => trim($start . ($start && $end ? ' – ' : '') . $end),
'details_url' => $pills[0]['url'] ?? (url('/leaderboard/dashboard') . '?' . http_build_query([
'id' => $board->id ?? '',
])),
'details_url' => $pills[0]['url'] ?? $detailsUrl,
'podium_users' => $pills[0]['podium_users'] ?? [],
'pills' => $pills,
];
@ -601,6 +648,38 @@ $data['custom_end_date'] = !empty($data['custom_end_date'])
return $users;
}
protected function isTeamLeaderboardView($flag): bool
{
return $flag === true || $flag === 1 || $flag === '1' || $flag === 'true' || $flag === 'yes';
}
protected function podiumUsersFromTeams(array $rows): array
{
usort($rows, function ($a, $b) {
$a = $this->asObject($a);
$b = $this->asObject($b);
return ((int) ($a->rank ?? 0)) <=> ((int) ($b->rank ?? 0));
});
$users = [];
foreach (array_slice($rows, 0, 3) as $row) {
$row = $this->asObject($row);
$team = $this->asObject($row->team ?? null);
$name = trim((string) ($team->name ?? ''));
if ($name === '') {
$name = 'Team';
}
$users[] = [
'name' => $name,
'initials' => $this->userInitials($name),
'avatar' => $team->icon ?? '',
'score' => is_numeric($row->score ?? null) ? (0 + $row->score) : 0,
];
}
return $users;
}
public function dashboardLeaderboardPodium(Request $request)
{
$data = $this->teamReportViewData($request);
@ -1013,4 +1092,46 @@ $data['custom_end_date'] = !empty($data['custom_end_date'])
$ts = strtotime((string) $value);
return $ts ? date('d M Y', $ts) : '';
}
protected function getUserAttributeOptions(): array
{
$options = [];
$userAttrs = $this->getlambda('get', 'ums', 'userattribute', 'get_all');
foreach (($userAttrs->data->items ?? []) as $attr) {
if (($attr->status ?? 'active') === 'disabled') {
continue;
}
$code = $attr->code ?? '';
if ($code === '') {
continue;
}
$options[$code] = $attr->attribute ?? $code;
}
return $options;
}
protected function normalizeAttributeCodes($raw): array
{
if (is_string($raw) && $raw !== '') {
$decoded = json_decode($raw, true);
$raw = is_array($decoded) ? $decoded : explode('#', $raw);
}
if (!is_array($raw)) {
return [];
}
$codes = [];
foreach ($raw as $code) {
if (is_object($code)) {
$code = $code->code ?? '';
}
$code = trim((string) $code);
if ($code !== '') {
$codes[] = $code;
}
}
return array_values(array_unique($codes));
}
}

View File

@ -0,0 +1,348 @@
<?php
namespace App\Http\Controllers;
use App\Exports\ZenovaReportExport;
use App\Helpers\ZenovaReportAccess;
use App\Http\Requests\LambdaRequest;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
class ZenovaReportController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->middleware(function ($request, $next) {
if (!ZenovaReportAccess::canView()) {
abort(403);
}
return $next($request);
});
$this->_service = 'reports';
$this->_module = 'zenova_reports';
$this->_viewname = 'zenovareport';
$this->_title = 'Glipiq & Sitazit Dashboard';
$this->_base_url = '/zenovareport';
}
public function list(Request $request)
{
return redirect('/zenovareport/dashboard');
}
public function dashboard(Request $request)
{
$level = $this->level($request);
$built = $this->build($level);
return view('zenovareport.dashboard', [
'title' => $this->_title,
'level' => $level,
'groups' => $built['groups'],
'body' => $built['body'],
'totals' => $built['totals'],
'error' => $built['error'],
]);
}
public function export(Request $request)
{
$level = $this->level($request);
$built = $this->build($level);
if ($built['error']) {
return redirect('/zenovareport/dashboard?level=' . $level)->with('error', $built['error']);
}
return Excel::download(
new ZenovaReportExport($built['groups'], $built['raw_body'], $built['raw_totals']),
'glipiq-sitazit-level-' . $level . '.xlsx'
);
}
private function level(Request $request): int
{
$level = (int) $request->input('level', 3);
return ($level >= 1 && $level <= 5) ? $level : 3;
}
private function build(int $level): array
{
$response = LambdaRequest::signedrequest(
'get',
'reports',
'zenova_reports/glipiqsitazit_report',
['level' => (string) $level],
120
);
$rows = [];
$error = null;
if (!$response || ($response->status ?? '') !== 'success') {
$error = $response->message ?? 'Unable to load the Glipiq & Sitazit report.';
} else {
$rows = is_array($response->data ?? null) ? $response->data : [];
}
$groups = $this->groups($rows);
$columns = [];
foreach ($groups as $group) {
foreach ($group['columns'] as $column) {
$columns[] = $column;
}
}
$body = [];
$rawBody = [];
$rawTotals = array_fill(0, count($columns), 0);
foreach ($rows as $row) {
$line = [];
$rawLine = [];
foreach ($columns as $index => $column) {
$raw = $this->rawValue($row, $column);
if ($column['type'] !== 'text') {
$rawTotals[$index] += $raw;
}
$rawLine[] = $raw;
$line[] = $this->formatValue($raw, $column['type']);
}
$body[] = $line;
$rawBody[] = $rawLine;
}
$totals = [];
$rawTotalLine = [];
if (!empty($rows)) {
$fsos = $this->sumKey($rows, 'Demography & Details', 'No. of FSOs');
$glipiqActive = $this->sumKey($rows, 'Demography & Details', 'Glipiq Active FSOs');
$sitazitActive = $this->sumKey($rows, 'Demography & Details', 'Sitazit Active FSOs');
foreach ($columns as $index => $column) {
if ($column['type'] === 'text') {
$label = !empty($column['is_total']) ? 'Total' : '';
$totals[] = $label;
$rawTotalLine[] = $label;
continue;
}
if ($column['key'] === 'Glipiq Active FSO %') {
$percent = $fsos ? ($glipiqActive / $fsos) * 100 : 0;
$totals[] = $this->formatValue($percent, 'percent');
$rawTotalLine[] = $percent;
continue;
}
if ($column['key'] === 'Sitazit Active FSO %') {
$percent = $fsos ? ($sitazitActive / $fsos) * 100 : 0;
$totals[] = $this->formatValue($percent, 'percent');
$rawTotalLine[] = $percent;
continue;
}
$totals[] = $this->formatValue($rawTotals[$index], $column['type']);
$rawTotalLine[] = $rawTotals[$index];
}
}
return [
'groups' => $groups,
'body' => $body,
'totals' => $totals,
'raw_body' => $rawBody,
'raw_totals' => $rawTotalLine,
'error' => $error,
];
}
private function groups(array $rows): array
{
$nameKey = 'Name of Region';
foreach ($rows as $row) {
$demo = (array) ($row->{'Demography & Details'} ?? []);
foreach (array_keys($demo) as $key) {
if (strpos($key, 'Name of ') === 0) {
$nameKey = $key;
break 2;
}
}
}
$demoColumns = $this->geoColumns($nameKey);
$currentType = strtolower(preg_replace('/^Name of /', '', $nameKey));
if (!in_array($currentType, ['zone', 'region', 'area'], true)) {
$nameColumn = $this->column($nameKey, 'Demography & Details', $nameKey, 'text');
$nameColumn['is_total'] = true;
$demoColumns[] = $nameColumn;
}
foreach ([
'No. of FSOs' => 'number',
'No. of DSMs' => 'number',
'Glipiq Active FSOs' => 'number',
'Glipiq Active FSO %' => 'percent',
'Sitazit Active FSOs' => 'number',
'Sitazit Active FSO %' => 'percent',
] as $key => $type) {
$demoColumns[] = $this->column($key, 'Demography & Details', $key, $type);
}
$dayMetrics = ['GLIPIQ Total Rx', 'ABSI Camp Count', 'Patients Screened Count', 'Sitazit Total RX'];
$periodMetrics = [
'GLIPIQ Total Rx',
'GLIPIQ New Conversion',
'ABSI Camp Count',
'Patients Screened Count',
'GLIPIQ Total POB Units',
'Sitazit Total RX',
'Sitazit New Conversions',
'Sitazit Total POB Units',
];
$groups = [
['label' => 'Demography & Details', 'tone' => 'demo', 'columns' => $demoColumns],
];
$first = isset($rows[0]) ? $rows[0] : null;
foreach ([
'day5' => 'Day 5 Status',
'day4' => 'Day 4 Status',
'day3' => 'Day 3 Status',
'day2' => 'Day 2 Status',
] as $section => $title) {
$groups[] = [
'label' => $this->periodLabel($title, $this->sectionField($first, $section, 'date')),
'tone' => 'day',
'columns' => $this->metricColumns($section, $dayMetrics),
];
}
$groups[] = [
'label' => $this->periodLabel('Day 1 / Yesterday', $this->sectionField($first, 'day1', 'date')),
'tone' => 'yesterday',
'columns' => $this->metricColumns('day1', $dayMetrics),
];
$groups[] = [
'label' => $this->periodLabel('Today', $this->sectionField($first, 'today', 'date')),
'tone' => 'today',
'columns' => $this->metricColumns('today', $periodMetrics),
];
$groups[] = [
'label' => $this->periodLabel('MTD', $this->sectionField($first, 'mtd', 'period')),
'tone' => 'mtd',
'columns' => $this->metricColumns('mtd', $periodMetrics),
];
$groups[] = [
'label' => $this->periodLabel('YTD', $this->sectionField($first, 'ytd', 'period')),
'tone' => 'ytd',
'columns' => $this->metricColumns('ytd', $periodMetrics),
];
return $groups;
}
private function geoColumns($nameKey)
{
$currentType = preg_replace('/^Name of /', '', $nameKey);
$columns = [];
foreach (['Zone', 'Region', 'Area'] as $type) {
if (strcasecmp($currentType, $type) === 0) {
$column = $this->column($type, 'Demography & Details', $nameKey, 'text');
$column['is_total'] = true;
$columns[] = $column;
continue;
}
$columns[] = $this->column($type, 'parent_positions', $type, 'text');
}
return $columns;
}
private function column(string $label, string $section, string $key, string $type): array
{
return [
'label' => $label,
'section' => $section,
'key' => $key,
'type' => $type,
];
}
private function metricColumns(string $section, array $metrics): array
{
$columns = [];
foreach ($metrics as $metric) {
$columns[] = $this->column($metric, $section, $metric, 'number');
}
return $columns;
}
private function periodLabel(string $title, string $period): string
{
return $period !== '' ? $title . ' (' . $period . ')' : $title;
}
private function sectionField($row, $section, $field)
{
if (!is_object($row) || !isset($row->{$section}) || !is_object($row->{$section})) {
return '';
}
$value = $row->{$section}->{$field} ?? '';
return $value === null ? '' : (string) $value;
}
private function rawValue($row, array $column)
{
if ($column['section'] === 'parent_positions') {
$parents = (array) ($row->parent_positions ?? []);
foreach ($parents as $key => $parentValue) {
if (strcasecmp((string) $key, 'Country') === 0) {
continue;
}
if (strcasecmp((string) $key, $column['key']) === 0) {
return ($parentValue === null || $parentValue === '') ? '' : (string) $parentValue;
}
}
return '';
}
$section = $row->{$column['section']} ?? null;
$value = is_object($section) ? ($section->{$column['key']} ?? null) : null;
if ($column['type'] === 'text') {
return ($value === null || $value === '') ? '-' : (string) $value;
}
return is_numeric($value) ? (float) $value : 0;
}
private function sumKey(array $rows, string $section, string $key): float
{
$total = 0;
foreach ($rows as $row) {
$value = $row->{$section}->{$key} ?? 0;
if (is_numeric($value)) {
$total += (float) $value;
}
}
return $total;
}
private function formatValue($value, $type)
{
if ($type === 'text') {
return (string) $value;
}
$number = (float) $value;
if ($type === 'percent') {
return number_format($number, 2) . '%';
}
if (floor($number) == $number) {
return number_format($number, 0);
}
return number_format($number, 2);
}
}

View File

@ -34,9 +34,9 @@ class LambdaRequest
case self::REMOTE_ENV != 'local' && $service == 'metric' && $url == 'metric_report/view':
return 'https://zbmttswaqimy3nmo5wactmja3i0uosyv.lambda-url.ap-south-1.on.aws/';
break;
case self::REMOTE_ENV != 'local' && $service == 'form' && $url == 'survey/getformhistory':
return 'https://tvp2drn5c2jpf7dpxdwwykdez40ikfbz.lambda-url.ap-south-1.on.aws/';
break;
// case self::REMOTE_ENV != 'local' && $service == 'form' && $url == 'survey/getformhistory':
// return 'https://tvp2drn5c2jpf7dpxdwwykdez40ikfbz.lambda-url.ap-south-1.on.aws/';
// break;
case self::REMOTE_ENV != 'local' && $service == 'form' && $url == 'formentity/getformhistory':
return 'https://sgrmuxa46z7xfouezyvg3tnhnq0yptjn.lambda-url.ap-south-1.on.aws/';
break;
@ -46,9 +46,9 @@ class LambdaRequest
case self::REMOTE_ENV != 'local' && $service == 'catalog' && $url == 'activity/nyexecutionfollowup':
return 'https://4sat7sjmmslqwq6nil2kxjxm6y0afoii.lambda-url.ap-south-1.on.aws/';
break;
case self::REMOTE_ENV != 'local' && $service == 'form' && $url == 'quiz/getformhistory':
return 'https://pc4gexzhd43hwms34p7vldwksm0zoehu.lambda-url.ap-south-1.on.aws/';
break;
// case self::REMOTE_ENV != 'local' && $service == 'form' && $url == 'quiz/getformhistory':
// return 'https://pc4gexzhd43hwms34p7vldwksm0zoehu.lambda-url.ap-south-1.on.aws/';
// break;
case self::REMOTE_ENV != 'local' && $service == 'magazine' && $url == 'magazine/magazine_export':
return 'https://bmilwmrobqef6z6pxmb7aa2tui0pcrca.lambda-url.ap-south-1.on.aws/';
break;

View File

@ -55,6 +55,70 @@ class InceptNebAllocation
];
}
public function summaryAll()
{
$allocated = [
'robust' => (int) DB::table('incept_neb_allocations')->sum('robust_qty'),
'mini' => (int) DB::table('incept_neb_allocations')->sum('mini_qty'),
'control_d' => (int) DB::table('incept_neb_allocations')->sum('control_d_qty'),
];
$usedByType = [];
$rows = DB::table('incept_neb_usage')
->select('nebulizer_type', DB::raw('SUM(qty) as used_qty'))
->groupBy('nebulizer_type')
->get();
foreach ($rows as $row) {
$usedByType[$row->nebulizer_type] = (int) $row->used_qty;
}
$types = [];
foreach (self::TYPES as $key => $meta) {
$qty = (int) ($allocated[$key] ?? 0);
$used = (int) ($usedByType[$key] ?? 0);
$types[$key] = [
'label' => $meta['label'],
'allocated' => $qty,
'used' => $used,
'balance' => max(0, $qty - $used),
];
}
return ['types' => $types];
}
public function viewerSeesOrgTotals($formdata)
{
$allAccess = $this->formValue($formdata, 'all_data_access');
if ($allAccess === true || $allAccess === 1 || $allAccess === '1' || $allAccess === 'true') {
return true;
}
$designation = '';
if (is_object($formdata) && isset($formdata->user_info->designation)) {
$designation = (string) $formdata->user_info->designation;
}
if (trim($designation) === '') {
$empId = $this->resolveEmpId([], is_object($formdata) ? $formdata : null);
if ($empId !== '') {
$allocation = DB::table('incept_neb_allocations')->where('emp_id', $empId)->first();
$designation = $allocation->designation ?? '';
}
}
return strtoupper(trim($designation)) === 'HO';
}
public function withOrgTotals(array $summary, $formdata)
{
if (!$this->viewerSeesOrgTotals($formdata)) {
return $summary;
}
$summary['show_org_totals'] = true;
$summary['org_totals'] = $this->summaryAll();
return $summary;
}
public function mslUsage($mslCode)
{
$mslCode = trim((string) $mslCode);
@ -222,6 +286,15 @@ class InceptNebAllocation
DB::table('incept_neb_usage')->where('id', $usageId)->delete();
}
public function releaseBySubmission($submissionId)
{
$submissionId = trim((string) $submissionId);
if ($submissionId === '') {
return;
}
DB::table('incept_neb_usage')->where('submission_id', $submissionId)->delete();
}
public function typeKey($label)
{
$value = strtolower(trim((string) $label));
@ -256,6 +329,18 @@ class InceptNebAllocation
return $value;
}
private function formValue($formdata, $key)
{
if (is_object($formdata) && isset($formdata->{$key})) {
return $formdata->{$key};
}
if (is_array($formdata) && array_key_exists($key, $formdata)) {
return $formdata[$key];
}
return null;
}
private function acquireLock($name)
{
$name = substr($name, 0, 64);

View File

@ -0,0 +1,186 @@
@extends('layouts.vertical', ["page_title"=> "Add & Edit Action Sheets"])
@section('css')
<link href="{{asset('assets/libs/select2/select2.min.css')}}" rel="stylesheet" type="text/css" />
@endsection
@section('content')
<div class="container-fluid">
@if(session()->has('message'))
<div class="alert alert-success mt-3">
{{ session()->get('message') }}
</div>
@endif
@if(session()->has('error'))
<div class="alert alert-danger mt-3">
{{ session()->get('error') }}
</div>
@endif
<div class="row mt-3">
<div class="col-xl-12">
<div class="card">
<form method="post" action="" id="form-actionsheet" enctype="multipart/form-data">
@csrf
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-3">
<h4 class="page-title">Add / Edit {{$title}}</h4>
</div>
<div class="col-md-6 mb-3">
<div class="float-end">
<a href="{{$base_url}}s" class="btn btn-dark waves-effect waves-light">Back</a>
<button type="submit" class="btn btn-success waves-effect waves-light">Submit</button>
</div>
</div>
@foreach($fields as $field => $props)
@if($props['type'] != 'hidden')
<div class="col-md-{{isset($props['fullwidth'])?'12':'6'}} mb-3">
<label for="field-{{$field}}" class="form-label">{{$props['title']}} {!!isset($props['required'])&&$props['required'] ? '<span class="text-danger">*</span>':''!!}</label>
@endif
@switch($props['type'])
@case('hidden')
<input type="hidden" id="field-{{$field}}" name="{{$field}}" value="{{$mode=='edit' && isset($item->{$field}) ? $item->{$field} : ''}}">
@break
@case('text')
<input type="text" id="field-{{$field}}" name="{{$field}}" class="form-control" placeholder="Enter {{$props['title']}}" value="{{$mode=='edit' && isset($item->{$field}) ? $item->{$field} : ''}}" {{isset($props['required']) && $props['required'] ? 'required' : ''}}>
@break
@case('number')
<input type="number" id="field-{{$field}}" name="{{$field}}" class="form-control" placeholder="Enter {{$props['title']}}" value="{{$mode=='edit' && isset($item->{$field}) ? $item->{$field} : ($props['default'] ?? '')}}" {{isset($props['required']) && $props['required'] ? 'required' : ''}}>
@break
@case('date')
<input type="date" id="field-{{$field}}" name="{{$field}}" class="form-control" value="{{$mode=='edit' && isset($item->{$field}) ? $item->{$field} : ''}}" {{isset($props['required']) && $props['required'] ? 'required' : ''}}>
@break
@case('textarea')
<textarea id="field-{{$field}}" name="{{$field}}" class="form-control {{ $field === 'html_format' ? 'html-format' : '' }}" rows="{{ $field === 'html_format' ? 16 : 4 }}" placeholder="Enter {{$props['title']}}" {{isset($props['required']) && $props['required'] ? 'required' : ''}}>{{$mode=='edit' && isset($item->{$field}) ? $item->{$field} : ''}}</textarea>
@if($field === 'html_format')
<small class="text-muted">Use each metric id as a placeholder, for example {Ciphaler}.</small>
@endif
@break
@case('select')
@php
$selected = ($mode == 'edit' && isset($item->{$field}) && $item->{$field} !== '') ? $item->{$field} : ($props['default'] ?? '');
@endphp
<select class="form-control" id="field-{{$field}}" name="{{$field}}" {{isset($props['required']) && $props['required'] ? 'required' : ''}}>
<option value=""> -- Select {{$props['title']}} -- </option>
@foreach($props['values'] as $k => $v)
<option value="{{$k}}" {{ (string) $selected === (string) $k ? 'selected' : '' }}>{{$v}}</option>
@endforeach
</select>
@break
@case('multiselect')
<select class="form-control select2-multiple" data-toggle="select2" data-width="100%" multiple="multiple" data-placeholder="Choose {{$props['title']}}" id="field-{{$field}}" name="{{$field}}[]" {{isset($props['required']) && $props['required'] ? 'required' : ''}}>
@foreach($props['values'] as $k => $v)
<option value="{{$k}}" {{$mode=='edit' && isset($item->{$field}) && is_array($item->{$field}) && in_array($k, $item->{$field}) ? 'selected' : ''}}>{{$v}}</option>
@endforeach
</select>
@break
@case('sheet_metrics')
<div class="d-flex justify-content-between align-items-center mb-2">
<small class="text-muted">Metric id is the HTML placeholder key.</small>
<button type="button" class="btn btn-info btn-xs" id="btn-add-metric">Add Metric</button>
</div>
<div class="table-responsive">
<table class="table mb-0" id="table-sheet-metrics">
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>Label</th>
<th>Metrics</th>
<th></th>
</tr>
</thead>
<tbody>
@php
$sheetMetrics = ($mode == 'edit' && isset($item->metrics) && is_array($item->metrics)) ? $item->metrics : [];
@endphp
@foreach($sheetMetrics as $i => $map)
<tr>
<td><input type="text" name="metrics[{{$i}}][id]" class="form-control" value="{{ $map['id'] ?? '' }}" placeholder="Ciphaler"></td>
<td><input type="text" name="metrics[{{$i}}][title]" class="form-control" value="{{ $map['title'] ?? '' }}"></td>
<td><input type="text" name="metrics[{{$i}}][label]" class="form-control" value="{{ $map['label'] ?? '' }}"></td>
<td>
<select class="form-control metric-ids-select" data-width="100%" multiple="multiple" data-placeholder="Choose metrics" name="metrics[{{$i}}][metric_ids][]">
@foreach($props['metrics'] as $k => $v)
<option value="{{$k}}" {{ isset($map['metric_ids']) && is_array($map['metric_ids']) && in_array($k, $map['metric_ids']) ? 'selected' : '' }}>{{$v}}</option>
@endforeach
</select>
</td>
<td><button type="button" class="btn btn-danger waves-effect waves-light btn-remove-metric"><i class="mdi mdi-close"></i></button></td>
</tr>
@endforeach
</tbody>
</table>
</div>
@break
@endswitch
@if($props['type'] != 'hidden')
</div>
@endif
@endforeach
<div class="col-md-12 mb-3">
<div class="float-end">
<a href="{{$base_url}}s" class="btn btn-dark waves-effect waves-light">Back</a>
<button type="submit" class="btn btn-success waves-effect waves-light">Submit</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<style>
.html-format { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 12px; }
</style>
@endsection
@section('script')
<script src="{{asset('assets/libs/select2/select2.min.js')}}"></script>
<script type="text/javascript">
$(document).ready(function(){
var metricOptions = @json($fields['metrics']['metrics'] ?? []);
var rowIndex = $('#table-sheet-metrics tbody tr').length;
$('[data-toggle="select2"]').select2();
function initMetricSelects($context) {
($context || $(document)).find('select.metric-ids-select').each(function() {
var $select = $(this);
if ($select.hasClass('select2-hidden-accessible')) {
$select.select2('destroy');
}
$select.select2({
width: '100%',
placeholder: 'Choose metrics'
});
});
}
initMetricSelects();
$('#btn-add-metric').on('click', function() {
var index = rowIndex++;
var options = '';
Object.keys(metricOptions).forEach(function(id) {
options += '<option value="' + $('<div>').text(id).html() + '">' + $('<div>').text(metricOptions[id]).html() + '</option>';
});
var row = ''
+ '<tr>'
+ '<td><input type="text" name="metrics[' + index + '][id]" class="form-control" placeholder="Ciphaler"></td>'
+ '<td><input type="text" name="metrics[' + index + '][title]" class="form-control"></td>'
+ '<td><input type="text" name="metrics[' + index + '][label]" class="form-control"></td>'
+ '<td><select class="form-control metric-ids-select" multiple="multiple" name="metrics[' + index + '][metric_ids][]">' + options + '</select></td>'
+ '<td><button type="button" class="btn btn-danger waves-effect waves-light btn-remove-metric"><i class="mdi mdi-close"></i></button></td>'
+ '</tr>';
$('#table-sheet-metrics tbody').append(row);
initMetricSelects($('#table-sheet-metrics tbody tr').last());
});
$('#table-sheet-metrics').on('click', '.btn-remove-metric', function() {
$(this).closest('tr').remove();
});
});
</script>
@endsection

View File

@ -15,7 +15,7 @@
@endif
<div class="col-sm-6 col-md-4 col-xl-3">
<div class="card">
<div class="card-body action-card" url="{{$action->url}}" style="cursor: pointer;">
<div class="card-body action-card" data-url="{{$action->url}}" style="cursor: pointer;">
<img src="{{$action->icon}}" alt="image" class="img-fluid rounded" width="200" />
<p class="mt-1 text-truncate">{{$action->name}}</p>
</div>
@ -65,8 +65,11 @@
<script type="text/javascript">
$(document).ready(function(){
$('.action-card').click(function(){
var src = $(this).attr('data-url');
$('#scrollable-modal #scrollableModalTitle').html( $(this).find('.text-truncate').html() );
$('#scrollable-modal .modal-body').html('<iframe class="responsive-iframe" src="'+ $(this).attr('url') +'" frameborder="0"></iframe>')
$('#scrollable-modal .modal-body').empty().append(
$('<iframe>', { 'class': 'responsive-iframe', frameborder: 0, src: src })
);
$('#scrollable-modal').modal('show')
})
});

View File

@ -94,6 +94,19 @@
<th data-priority="1">{{$question->label}}</th>
@endif
@endforeach
@php
$showGeneratedImage = false;
foreach ($submissions ?? [] as $submission) {
$generatedFiles = $submission->formdata->genareted_image ?? null;
if (!empty($generatedFiles)) {
$showGeneratedImage = true;
break;
}
}
@endphp
@if($showGeneratedImage)
<th data-priority="1">Generated Image</th>
@endif
</tr>
</thead>
<tbody>
@ -285,6 +298,18 @@
{ "data": "{{$code}}" },
@endif
@endforeach
@if($showGeneratedImage)
{
"data": "generated_image_url",
render: function(data) {
if (!data) return '';
return data.split(', ').map(function(url) {
var name = url.split('/').pop().split('?')[0];
return '<a class="btn btn-primary btn-xs" href="' + url + '" download="' + name + '">Download</a>';
}).join(' ');
}
},
@endif
],
"initComplete": function(settings, json) {
var textBox = $('.dataTables_filter label input');

View File

@ -0,0 +1,157 @@
<!DOCTYPE html>
<html lang="en">
<head>
@include('layouts.shared/title-meta', ['title' => "History"])
@include('layouts.shared/head-css', ["mode" => $mode ?? '', "demo" => $demo ?? ''])
<style>
.incept-neb-table { font-size: 13px; margin-bottom: 4px; background: #fff; color: #212529; }
.incept-neb-table th, .incept-neb-table td { text-align: center; vertical-align: middle; padding: 6px 8px; }
.incept-neb-table thead th, .incept-neb-table tbody th { background: #0b3a4a; color: #fff; font-weight: 600; }
.incept-neb-table tbody td { color: #212529; background: #fff; }
</style>
</head>
<body class="loading">
<div class="account-pages">
<div class="container">
<!-- start page title -->
<div class="row">
<div class="col-8 p-2">
<h4 class="page-title">{{$formname}}</h4>
</div>
<div class="col-4 p-2">
<a href="/form/show" class="float-end pt-1">New Submission</a>
</div>
</div>
<!-- end page title -->
@if(session()->has('message'))
<div class="alert alert-success">
{{ session()->get('message') }}
</div>
@endif
@if(session()->has('error'))
<div class="alert alert-danger">
{{ session()->get('error') }}
</div>
@endif
<div class="row">
<div class="col-12 p-0">
<div class="card">
<div class="card-body">
@if(!empty($incept_neb['show_org_totals']))
@include('form.rapport-team3.partials.incept-neb-org-totals', ['orgTypes' => $incept_neb['org_totals']['types'] ?? []])
@endif
<div class="responsive-table-plugin">
<div class="table-rep-plugin">
<div class="table-responsive" data-pattern="priority-columns">
<table id="tech-companies-1" class="table table-striped">
<thead>
<tr>
<th data-priority="1">Submission Date</th>
@foreach(($userAttrs ?? []) as $attr)
<th data-priority="1">{{ $userAttrLabels[$attr] ?? ucwords(str_replace('_', ' ', $attr)) }}</th>
@endforeach
@foreach($questions as $code => $question)
@if($question->type == 'hidden')
@continue;
@endif
@if($question->type == 'multitextbox')
@foreach($question->options as $k => $v)
<th data-priority="1">{{ is_object($v) ? ($v->display ?? $v->label ?? $k) : $v }} ({{$question->label}})</th>
@endforeach
@else
<th data-priority="1">{{$question->label}}</th>
@endif
@endforeach
<th data-priority="1">Action</th>
</tr>
</thead>
<tbody>
@foreach($submissions as $submission)
<tr>
<th>{{date("d M Y", strtotime($submission->updatedAt))}}</th>
@foreach(($userAttrs ?? []) as $attr)
@php
$uaUser = $submission->user_info ?? null;
$uaVal = '-';
if ($uaUser) {
if (isset($uaUser->{$attr}) && $uaUser->{$attr} !== '') {
$uaVal = $uaUser->{$attr};
} elseif (isset($uaUser->{'attribute_'.$attr}) && $uaUser->{'attribute_'.$attr} !== '') {
$uaVal = $uaUser->{'attribute_'.$attr};
}
}
@endphp
<td>{{ $uaVal }}</td>
@endforeach
@foreach($questions as $code => $question)
@if($question->type == 'hidden')
@continue;
@endif
@if($question->type == 'file')
<td><a target="_blank" href="{{ isset($submission->formdata->{$code}) ? is_array($submission->formdata->{$code})? implode(', ', $submission->formdata->{$code}) :(is_string($submission->formdata->{$code}) ? $submission->formdata->{$code} : '' ) : $code }}" download="{{ isset($submission->formdata->{$code}) ? is_array($submission->formdata->{$code})? implode(', ', $submission->formdata->{$code}) : (is_string($submission->formdata->{$code}) ? $submission->formdata->{$code} : '' ) : $code }}" class="btn btn-dark btn-xs"><i class="mdi mdi-cloud-download me-1"></i> Download</a></td>
@elseif($question->type == 'radiometrix' && isset($submission->formdata->{$code}) && is_object($submission->formdata->{$code}))
<td>
{{ collect((array)$submission->formdata->{$code})->map(function($val, $key){ return "$key: $val"; })->implode(', ') }}
</td>
@elseif($question->type == 'multitextbox')
@foreach($question->options as $k => $v)
@php
$optKey = is_object($v) ? ($v->value ?? $k) : $k;
$optVal = '';
if (isset($submission->formdata->{$optKey})) {
$raw = $submission->formdata->{$optKey};
$optVal = is_array($raw) ? implode(', ', $raw) : (is_object($raw) ? json_encode($raw) : $raw);
} elseif (isset($submission->formdata->{$code}) && (is_object($submission->formdata->{$code}) || is_array($submission->formdata->{$code}))) {
$box = (array) $submission->formdata->{$code};
if (array_key_exists($optKey, $box)) {
$raw = $box[$optKey];
$optVal = is_array($raw) ? implode(', ', $raw) : (is_object($raw) ? json_encode($raw) : $raw);
}
}
@endphp
<td>{{ $optVal }}</td>
@endforeach
@elseif(count((array)$question->options) > 0 && 1==2 )
<td>{{ $question->options->{ $submission->formdata->{$code} } }}</td>
@else
<td>{{ isset($submission->formdata->{$code}) ? is_array($submission->formdata->{$code})? implode(', ', $submission->formdata->{$code}) : (is_object($submission->formdata->{$code}) ? collect((array)$submission->formdata->{$code})->map(function($val, $key){ return "$key: $val"; })->implode(', ') : $submission->formdata->{$code}) : '' }}</td>
@endif
@endforeach
<th>
@php
$isApproved = ($submission->workflow ?? '') == 'yes' && ($submission->workflow_status ?? '') == 'approved';
$showFieldDelete = isset($form->allow_delete_by_user) && $form->allow_delete_by_user == 'yes' && !$isApproved;
@endphp
@if($isApproved)
<p>Approved</p>
@endif
@if($showFieldDelete || !empty($can_delete_submission))
<a href="/form/delete?form_id={{$submission->formdata->form_id}}&id={{$submission->id}}" onclick="return confirm('Are you sure you want to delete this submission? This is not recoverable!');" class="btn btn-danger btn-xs mt-1">Delete</a>
@endif
</th>
</tr>
@endforeach
</tbody>
</table>
</div> <!-- end .table-responsive -->
</div> <!-- end .table-rep-plugin-->
</div> <!-- end .responsive-table-plugin-->
</div> <!-- end card body-->
</div> <!-- end card -->
</div><!-- end col-->
</div>
<!-- end row-->
</div> <!-- container -->
</div>
<!-- end page -->
@include('layouts.shared/footer-script')
</body>
</html>

View File

@ -42,6 +42,8 @@
@php
$incept = (isset($additional) && isset($additional['incept_neb'])) ? $additional['incept_neb'] : null;
$inceptTypes = $incept['types'] ?? [];
$showOrgTotals = !empty($incept['show_org_totals']);
$orgTypes = $incept['org_totals']['types'] ?? [];
@endphp
<style>
.incept-neb-table { font-size: 13px; margin-bottom: 4px; background: #fff; color: #212529; }
@ -49,6 +51,11 @@
.incept-neb-table thead th, .incept-neb-table tbody th { background: #0b3a4a; color: #fff; font-weight: 600; }
.incept-neb-table tbody td { color: #212529; background: #fff; }
</style>
@if($showOrgTotals)
<div class="col-12 mb-2">
@include('form.rapport-team3.partials.incept-neb-org-totals', ['orgTypes' => $orgTypes])
</div>
@endif
<div class="col-12 mb-2" id="incept-neb-panel"
data-robust="{{ $inceptTypes['robust']['balance'] ?? 0 }}"
data-mini="{{ $inceptTypes['mini']['balance'] ?? 0 }}"
@ -86,7 +93,7 @@
</tbody>
</table>
</div>
@if(empty($incept['has_allocation']))
@if(empty($incept['has_allocation']) && !$showOrgTotals)
<p class="text-danger small mb-1" id="incept-missing">No nebulizer allocation is set for this employee.</p>
@else
<p class="text-danger small mb-1" id="incept-missing" style="display:none;"></p>

View File

@ -0,0 +1,421 @@
<!DOCTYPE html>
<html lang="en">
<head>
@include('layouts.shared/title-meta', ['title' => "Doctor & Chemist Survey"])
@include('layouts.shared/head-css', ["mode" => $mode ?? '', "demo" => $demo ?? ''])
<link href="{{asset('assets/libs/sweetalert2/sweetalert2.min.css')}}" rel="stylesheet" type="text/css" />
<style>
.quick-action-form-card .form-control { font-size: 16px; }
.survey-section { border: 1px solid #d9d9d9; border-radius: 0.25rem; margin-bottom: 0.75rem; }
.survey-section > .section-head {
display: flex; width: 100%; text-align: left; background: #e5eef7; color: var(--bs-secondary);
border: 0; padding: 0.85rem 1rem; font-weight: 600;
}
.survey-section.locked > .section-head { opacity: 0.55; }
.survey-section .section-body { padding: 0.75rem 1rem 0.25rem; }
.rcpa-doctors { width: 100%; table-layout: fixed; border: 0; }
.table.rcpa-doctors > :not(caption) > * > * { vertical-align: middle; padding: 0.15rem; border: 0; width: 25%; box-shadow: none; }
.rcpa-doctors .form-control { font-size: 16px; min-width: 0; padding: 0.2rem 0.3rem; }
.survey-section.locked .section-body { display: none; }
#pageloader {
display: none;
position: fixed;
inset: 0;
z-index: 9999;
background: rgba(255, 255, 255, 0.8);
align-items: center;
justify-content: center;
}
</style>
</head>
<body class="loading">
<div id="pageloader">Loading</div>
<div class="account-pages">
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6 col-xl-5 p-0">
<div class="card bg-pattern quick-action-form-card">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<h4 class="page-title mb-0">Doctor & Chemist Survey</h4>
<a href="/form/history" class="pt-1">View History</a>
</div>
<form method="POST" action="/form/submit" id="report-form" enctype="multipart/form-data">
@csrf
<input type="hidden" name="form_id" id="field-form_id" value="1790230856-a5ff58a6-d509-4ac0-9b18-6d366a74965e">
<div class="mb-2">
<label class="form-label" for="field-record_date">Date <span class="text-danger">*</span></label>
<input type="date" class="form-control" id="field-record_date" name="record_date" required>
</div>
<div class="mb-3">
<label class="form-label" for="field-visit_type">Survey for <span class="text-danger">*</span></label>
<select class="form-control" id="field-visit_type" required>
<option value="doctor">Doctor</option>
<option value="chemist">Chemist</option>
</select>
</div>
<fieldset id="doctor-fields">
@include('form.rapport-team3.partials.doctor-chemist-doctor')
</fieldset>
<fieldset id="chemist-fields" disabled style="display:none">
@include('form.rapport-team3.partials.doctor-chemist-chemist')
</fieldset>
<button type="submit" class="btn btn-primary w-100 mt-2">Submit</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
@include('layouts.shared/footer-script')
<script src="{{asset('assets/libs/sweetalert2/sweetalert2.min.js')}}"></script>
<script>
$(function () {
var doctorFormId = '1790230856-a5ff58a6-d509-4ac0-9b18-6d366a74965e';
var chemistFormId = '1790230856-704c97b6-61f7-4eda-901a-2cb4ff6f293d';
var dateInput = document.getElementById('field-record_date');
var today = new Date();
var month = String(today.getMonth() + 1).padStart(2, '0');
var day = String(today.getDate()).padStart(2, '0');
dateInput.min = '2026-09-01';
dateInput.max = today.getFullYear() + '-' + month + '-' + day;
var surveyContacts = [];
var contactsReady = false;
function activeRoot() {
return $('#field-visit_type').val() === 'chemist' ? '#chemist-fields' : '#doctor-fields';
}
function contactPhone(contact) {
var digits = String((contact && contact.phone) || '').replace(/\D/g, '');
return digits.length > 10 ? digits.slice(-10) : digits;
}
function refreshSections() {
var root = $(activeRoot());
var open = true;
root.find('.survey-section').each(function () {
var section = $(this);
section.toggleClass('locked', !open);
section.find(':input').prop('disabled', !open);
if (!open) {
return;
}
var gates = section.find('[data-gate]');
if (gates.length && gates.filter(function () { return !$(this).val(); }).length) {
open = false;
}
});
syncOtherFields();
syncServiceFields();
}
function syncOtherFields() {
var pairs = [
['#field-degree', '#wrap-other_degree', '#field-other_degree'],
['#field-specialty', '#wrap-other_specialty', '#field-other_specialty']
];
pairs.forEach(function (pair) {
var show = /^others?$/i.test($(pair[0]).val() || '');
$(pair[1]).toggle(show);
var input = $(pair[2]);
input.prop('required', show);
if (!show) {
input.val('').prop('disabled', true);
} else if (!input.closest('.survey-section').hasClass('locked') && !$('#doctor-fields').prop('disabled')) {
input.prop('disabled', false);
}
});
}
function syncServiceFields() {
$('#chemist-fields .service-row').each(function () {
var row = $(this);
var show = row.find('.service-choice').val() === 'Yes' && !row.closest('.survey-section').hasClass('locked') && !$('#chemist-fields').prop('disabled');
var charge = row.find('.service-charge');
charge.toggle(show);
var select = charge.find('select');
select.prop('required', show).prop('disabled', !show);
if (!show) {
select.val('');
}
});
}
function fillSelect(select, contacts) {
var current = select.value;
select.innerHTML = '<option value="">-- Select --</option>';
contacts.forEach(function (item) {
var contact = item.contact || {};
var option = document.createElement('option');
option.value = item.value;
option.textContent = item.display || contact.name || '';
option.setAttribute('data-phone', contact.phone || '');
option.setAttribute('data-speciality', contact.speciality || '');
option.setAttribute('data-code', contact.msl_code || '');
option.setAttribute('data-location', contact.stockist_location || contact.location || '');
option.setAttribute('data-stockist-id', contact.stockist_id || '');
select.appendChild(option);
});
if (current) {
select.value = current;
}
}
function loadContacts() {
$.ajax({
type: 'GET',
url: '/form/getdoctorlist?partition={{ $form->partition ?? '' }}&hierarchy={{ $form->hierarchy_id ?? ($form->hierarchy ?? '') }}',
success: function (data) {
surveyContacts = Array.isArray(data) ? data : [];
contactsReady = true;
var doctors = surveyContacts.filter(function (item) {
return item.contact && String(item.contact.type || '').toLowerCase() === 'ebt_doctor';
});
var stockists = surveyContacts.filter(function (item) {
return item.contact && String(item.contact.type || '').toLowerCase() === 'ebt_stockist';
});
document.querySelectorAll('#chemist-fields .rcpa-doctor').forEach(function (select) {
fillSelect(select, doctors);
});
document.querySelectorAll('#chemist-fields .stockist-pick').forEach(function (select) {
fillSelect(select, stockists);
});
},
error: function () {
contactsReady = true;
}
});
}
function applyDoctor(select) {
var row = $(select).closest('tr');
var option = select.options[select.selectedIndex];
var chosen = select.value;
if (chosen) {
var duplicate = false;
$('#chemist-fields .rcpa-doctor[data-product="' + select.getAttribute('data-product') + '"]').each(function () {
if (this !== select && this.value === chosen) {
duplicate = true;
}
});
if (duplicate) {
select.value = '';
option = select.options[0];
fail('This doctor is already selected for this product.');
}
}
row.find('.rcpa-name').val(select.value ? option.textContent : '');
row.find('.rcpa-mobile').val(select.value ? (option.getAttribute('data-phone') || '') : '');
row.find('.rcpa-speciality').val(select.value ? (option.getAttribute('data-speciality') || '') : '');
row.find('.rcpa-code').val(select.value ? (option.getAttribute('data-code') || '') : '');
}
function applyStockist(select) {
var block = $(select).closest('.stockist-block');
var option = select.options[select.selectedIndex];
var chosen = select.value;
if (chosen) {
var duplicate = false;
$('#chemist-fields .stockist-pick').each(function () {
if (this !== select && this.value === chosen) {
duplicate = true;
}
});
if (duplicate) {
select.value = '';
option = select.options[0];
fail('This stockist is already selected.');
}
}
block.find('.stockist-name').val(select.value ? option.textContent : '');
block.find('.stockist-location').val(select.value ? (option.getAttribute('data-location') || '') : '');
block.find('.stockist-id').val(select.value ? (option.getAttribute('data-stockist-id') || '') : '');
}
function setVisit(type) {
var doctor = type !== 'chemist';
$('#doctor-fields').prop('disabled', !doctor).toggle(doctor);
$('#chemist-fields').prop('disabled', doctor).toggle(!doctor);
$('#field-form_id').val(doctor ? doctorFormId : chemistFormId);
$('#field-doctor_name').val(doctor ? ($('#field-doctor_name').val() || 'Dr. ') : $('#field-doctor_name').val());
refreshSections();
}
$('#field-visit_type').on('change', function () { setVisit(this.value); });
$('#report-form').on('input change', '.survey-section :input', refreshSections);
$('#chemist-fields').on('change', '.rcpa-doctor', function () { applyDoctor(this); refreshSections(); });
$('#chemist-fields').on('change', '.stockist-pick', function () { applyStockist(this); });
$('#chemist-fields').on('change', '.service-choice', syncServiceFields);
$('#field-degree, #field-specialty').on('change', syncOtherFields);
function fail(text) {
Swal.fire({ icon: 'error', title: 'Please check the form', text: text });
}
function alphaOk(value) {
return /^[A-Za-z][A-Za-z .]*$/.test(value);
}
function alnumOk(value) {
return /^[A-Za-z0-9]+$/.test(value);
}
function mobileTaken(type, mobile) {
return surveyContacts.some(function (item) {
var contact = item.contact || {};
return String(contact.type || '').toLowerCase() === type && contactPhone(contact) === mobile;
});
}
$('#report-form').on('submit', function (event) {
event.preventDefault();
var type = $('#field-visit_type').val();
var recordDate = $('#field-record_date').val();
if (!recordDate || recordDate < dateInput.min || recordDate > dateInput.max) {
fail('Date must be from 1 Sep 2026 through today.');
return;
}
if (type === 'doctor') {
var doctorName = $('#field-doctor_name').val().trim();
if (!alphaOk(doctorName)) {
fail('Doctor name can contain letters, spaces and periods only.');
return;
}
if (!/^[6-9][0-9]{9}$/.test($('#field-mobile_number').val())) {
fail('Mobile number must be 10 digits and start with 6, 7, 8 or 9.');
return;
}
var registration = $('#field-doctor_registration_number').val().trim();
if (registration && !alnumOk(registration)) {
fail('Registration number must be alphanumeric with no special characters.');
return;
}
var legacy = $('#field-legacy_code').val().trim();
if (legacy && !alnumOk(legacy)) {
fail('Legacy code must be alphanumeric with no special characters.');
return;
}
if (!contactsReady) {
fail('Contact list is still loading. Please try again.');
return;
}
if (mobileTaken('ebt_doctor', $('#field-mobile_number').val())) {
fail('A doctor with this mobile number already exists.');
return;
}
if (!alphaOk($('#field-doctor_location').val().trim())) {
fail('Doctor location can contain letters, spaces and periods only.');
return;
}
if (!/^[1-9][0-9]{5}$/.test($('#field-doctor_pincode').val())) {
fail('Doctor pincode must be 6 digits and must not start with 0.');
return;
}
if ($('#field-degree').val() === 'Other' && !alphaOk($('#field-other_degree').val().trim())) {
fail('Enter the other degree in letters only.');
return;
}
if ($('#field-specialty').val() === 'Other' && !alphaOk($('#field-other_specialty').val().trim())) {
fail('Enter the other specialty in letters only.');
return;
}
var photos = ['visiting_card', 'rx_pad', 'doctor_board'];
var hasPhoto = false;
for (var i = 0; i < photos.length; i++) {
var file = document.getElementById('field-' + photos[i]).files[0];
if (!file) continue;
hasPhoto = true;
if (!/\.(jpe?g|png)$/i.test(file.name) || file.size > 10 * 1024 * 1024) {
fail('Photos must be JPG, JPEG or PNG and no larger than 10 MB.');
return;
}
}
if (!hasPhoto) {
fail('Upload at least one of visiting card, Rx pad or doctor board.');
return;
}
} else {
if (!alphaOk($('#field-chemist_name').val().trim()) || !alphaOk($('#field-chemist_location').val().trim())) {
fail('Chemist name and location can contain letters, spaces and periods only.');
return;
}
if (!/^[6-9][0-9]{9}$/.test($('#field-chemist_mobile').val())) {
fail('Mobile number must be 10 digits and start with 6, 7, 8 or 9.');
return;
}
if (!contactsReady) {
fail('Contact list is still loading. Please try again.');
return;
}
if (mobileTaken('ebt_chemist', $('#field-chemist_mobile').val())) {
fail('A chemist with this mobile number already exists.');
return;
}
var missingDoctor = false;
$('#chemist-fields .rcpa-doctor[data-slot="1"]').each(function () {
if (!this.value) {
missingDoctor = true;
}
});
if (missingDoctor) {
fail('Select doctor 1 for each product.');
return;
}
var missingCharge = false;
$('#chemist-fields .service-choice').each(function () {
if ($(this).val() === 'Yes' && !$(this).closest('.service-row').find('.service-charge select').val()) {
missingCharge = true;
}
});
if (missingCharge) {
fail('Select Free or Chargeable for each service marked Yes.');
return;
}
if (!/^[1-9][0-9]{5}$/.test($('#field-chemist_pincode').val())) {
fail('Chemist pincode must be 6 digits and must not start with 0.');
return;
}
var board = document.getElementById('field-chemist_board').files[0];
if (!board || !/\.(jpe?g|png)$/i.test(board.name) || board.size > 10 * 1024 * 1024) {
fail('Chemist board must be a JPG, JPEG or PNG no larger than 10 MB.');
return;
}
}
$('#pageloader').css('display', 'flex').hide().fadeIn();
$.ajax({
type: 'POST',
url: '/form/ajaxsubmit',
data: new FormData($('#report-form')[0]),
processData: false,
contentType: false,
success: function (data) {
$('#pageloader').fadeOut();
if (data.status == 'success') {
Swal.fire({ title: 'Great!', text: 'Your data is successfully submitted!', icon: 'success' });
$('#report-form')[0].reset();
setVisit('doctor');
$('#field-visit_type').val('doctor');
} else {
Swal.fire({ icon: 'error', title: 'Data not submitted', text: 'Please try again.' });
}
},
error: function () {
$('#pageloader').fadeOut();
Swal.fire({ icon: 'error', title: 'Data not submitted', text: 'Please try again.' });
}
});
});
setVisit('doctor');
loadContacts();
});
</script>
</body>
</html>

View File

@ -0,0 +1,111 @@
@php
$chemistTypes = ['Attached','Floating','Semi','Chain Pharmacy'];
$products = ['Asthalin Inhaler' => 'asthalin_inhaler','Asthalin Rotacap' => 'asthalin_rotacap','Aerocort Inhaler' => 'aerocort_inhaler','Aerocort Rotacap' => 'aerocort_rotacap','Aerocort Forte Rotacap' => 'aerocort_forte_rotacap'];
$therapies = [
'Nebulization No. of Respules per Month (all included)' => 'neb_respules',
'Foracort Inhaler No. of Units per Month' => 'foracort_inhaler_units',
'Foracort Rotacaps No. of Units per Month' => 'foracort_rotacaps_units',
'Asthalin Syrup No. of Units per Month' => 'asthalin_syrup_units',
'Levolin Syrup No. of Units per Month' => 'levolin_syrup_units',
'Deriphyllin No. of Units per Month' => 'deriphyllin_units',
'Beclomethasone Tablet No. of Units per Month' => 'beclomethasone_units',
'Methylprednisolone Tablet No. of Units per Month' => 'methylprednisolone_units',
'Prednisolone Tablet No. of Units per Month' => 'prednisolone_units',
'Cough Syrups No. of Units per Month' => 'cough_syrups_units',
];
$services = ['BP Monitoring' => 'chemist_bp_monitoring','Weighing Machine' => 'chemist_weighing_machine','Nebulization' => 'chemist_nebulization'];
@endphp
<div class="survey-section">
<button type="button" class="section-head">Chemist details</button>
<div class="section-body">
<div class="mb-2"><label class="form-label">Chemist Name <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-chemist_name" name="chemist_name" data-gate required></div>
<div class="mb-2"><label class="form-label">Mobile Number <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-chemist_mobile" name="mobile_number" inputmode="numeric" maxlength="10" data-gate required></div>
<div class="mb-2"><label class="form-label">Chemist Location <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-chemist_location" name="chemist_location" data-gate required></div>
<div class="mb-2"><label class="form-label">Chemist Pincode <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-chemist_pincode" name="chemist_pincode" inputmode="numeric" maxlength="6" data-gate required></div>
<div class="mb-2"><label class="form-label">Chemist Type <span class="text-danger">*</span></label><select class="form-control" name="chemist_type" data-gate required><option value="">-- Select --</option>@foreach($chemistTypes as $item)<option value="{{$item}}">{{$item}}</option>@endforeach</select></div>
<div class="mb-2"><label class="form-label" for="field-chemist_manager_joint_visit">Manager (EBM) Joint Visit Status <span class="text-danger">*</span></label><select class="form-control" id="field-chemist_manager_joint_visit" name="manager_joint_visit" data-gate required><option value="">-- Select --</option><option value="Yes">Yes</option><option value="No">No</option></select></div>
</div>
</div>
<div class="survey-section locked">
<button type="button" class="section-head">Upload photo</button>
<div class="section-body">
<div class="mb-2"><label class="form-label">Chemist Board <span class="text-danger">*</span></label><input type="file" class="form-control" id="field-chemist_board" name="chemist_board" accept=".jpg,.jpeg,.png,image/jpeg,image/png" data-gate required></div>
</div>
</div>
<div class="survey-section locked">
<button type="button" class="section-head">RCPA details</button>
<div class="section-body">
@foreach($products as $label => $code)
<h6 class="mt-2">{{$label}}</h6>
<div class="mb-2"><label class="form-label">Number of Units per Month</label><input type="number" min="0" step="1" class="form-control" name="{{$code}}_units"></div>
<div class="table-responsive mb-3">
<table class="table table-sm align-middle mb-0 rcpa-doctors">
<thead>
<tr>
<th>Name</th>
<th>Mobile</th>
<th>Speciality</th>
<th>Code</th>
</tr>
</thead>
<tbody>
@foreach([1, 2, 3] as $doctor)
<tr data-product="{{$code}}">
<td>
<select class="form-control rcpa-doctor" data-product="{{$code}}" data-slot="{{$doctor}}" aria-label="Doctor {{$doctor}} name" @if($doctor === 1) data-gate required @endif>
<option value="">-- Select --</option>
</select>
<input type="hidden" name="{{$code}}_doctor_{{$doctor}}_name" class="rcpa-name">
</td>
<td><input type="text" class="form-control rcpa-mobile" name="{{$code}}_doctor_{{$doctor}}_mobile" readonly tabindex="-1" aria-label="Doctor {{$doctor}} mobile"></td>
<td><input type="text" class="form-control rcpa-speciality" name="{{$code}}_doctor_{{$doctor}}_speciality" readonly tabindex="-1" aria-label="Doctor {{$doctor}} speciality"></td>
<td><input type="text" class="form-control rcpa-code" name="{{$code}}_doctor_{{$doctor}}_code" readonly tabindex="-1" aria-label="Doctor {{$doctor}} code"></td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endforeach
</div>
</div>
<div class="survey-section locked">
<button type="button" class="section-head">Stockist</button>
<div class="section-body">
@foreach([1,2] as $n)
<div class="stockist-block mb-3" data-slot="{{$n}}">
<div class="mb-2"><label class="form-label">Stockist {{$n}} Name</label><select class="form-control stockist-pick" data-slot="{{$n}}"><option value="">-- Select --</option></select><input type="hidden" name="stockist_{{$n}}_name" class="stockist-name"></div>
<div class="mb-2"><label class="form-label">Stockist {{$n}} Location</label><input type="text" class="form-control stockist-location" name="stockist_{{$n}}_location" readonly tabindex="-1"></div>
<div class="mb-2"><label class="form-label">Stockist {{$n}} ID</label><input type="text" class="form-control stockist-id" name="stockist_{{$n}}_id" readonly tabindex="-1"></div>
<div class="mb-2"><label class="form-label">Frequency of Supply</label><input type="number" min="0" step="1" class="form-control" name="stockist_{{$n}}_frequency"></div>
</div>
@endforeach
</div>
</div>
<div class="survey-section locked">
<button type="button" class="section-head">Other therapies</button>
<div class="section-body">
@foreach($therapies as $label => $code)
<div class="mb-2"><label class="form-label">{{$label}}</label><input type="number" min="0" step="1" class="form-control" name="{{$code}}"></div>
@endforeach
</div>
</div>
<div class="survey-section locked">
<button type="button" class="section-head">Services</button>
<div class="section-body">
@foreach($services as $label => $code)
<div class="mb-2 service-row">
<label class="form-label">{{$label}}</label>
<select class="form-control service-choice" name="{{$code}}"><option value="">-- Select --</option><option value="Yes">Yes</option><option value="No">No</option></select>
<div class="mt-2 service-charge" style="display:none">
<select class="form-control" name="{{$code}}_charge" disabled><option value="">-- Select --</option><option value="Free">Free</option><option value="Chargeable">Chargeable</option></select>
</div>
</div>
@endforeach
</div>
</div>

View File

@ -0,0 +1,54 @@
@php
$degrees = ['B. Pharm','D. Pharm','M.B.B.S.','M.D.','B.A.M.S.','B.U.M.S.','B.H.M.S.','D.A.M.S.','D.H.M.S.','B.M.S.','D.M.S.','B.E.M.S.','R.M.P.','B.I.A.M.','D.I.A.M.','C.M.S.','C.M.S. & E.D.','C.C.M.P.','G.A.M.S.','B.P.T.','B.S.C.','Other'];
$specialties = ['G.P (General Physician)','R.M.P (Registered Medical Practitioner)','C.P (Consulting Physician)','Child Specialist','Pediatrician','Family Physician','Multi Speciality','Gynecologist','Urologist','Physiotherapy','Other'];
$bands = ['0-10','10-20','20-30','30-40','40-50','More than 50'];
$doctorTypes = ['Standalone Clinic','Nursing Home','Hospital','Dispensing Doctors (Doctor with Self Purchase)'];
$products = ['Asthalin Inhaler' => 'asthalin_inhaler','Asthalin Rotacap' => 'asthalin_rotacap','Aerocort Inhaler' => 'aerocort_inhaler','Aerocort Rotacap' => 'aerocort_rotacap','Aerocort Forte Rotacap' => 'aerocort_forte_rotacap'];
@endphp
<div class="survey-section">
<button type="button" class="section-head">Doctor details</button>
<div class="section-body">
<div class="mb-2"><label class="form-label">Doctor Name <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-doctor_name" name="doctor_name" value="Dr. " data-gate required></div>
<div class="mb-2"><label class="form-label">Registration Number</label><input type="text" class="form-control" id="field-doctor_registration_number" name="doctor_registration_number"></div>
<div class="mb-2"><label class="form-label">Mobile Number <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-mobile_number" name="mobile_number" inputmode="numeric" maxlength="10" data-gate required></div>
<div class="mb-2"><label class="form-label">Legacy Code</label><input type="text" class="form-control" id="field-legacy_code" name="legacy_code"></div>
<div class="mb-2"><label class="form-label">Degree <span class="text-danger">*</span></label><select class="form-control" id="field-degree" name="degree" data-gate required><option value="">-- Select --</option>@foreach($degrees as $item)<option value="{{$item}}">{{$item}}</option>@endforeach</select></div>
<div class="mb-2" id="wrap-other_degree" style="display:none"><label class="form-label">Other Degree <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-other_degree" name="other_degree" disabled></div>
<div class="mb-2"><label class="form-label">Specialty <span class="text-danger">*</span></label><select class="form-control" id="field-specialty" name="specialty" data-gate required><option value="">-- Select --</option>@foreach($specialties as $item)<option value="{{$item}}">{{$item}}</option>@endforeach</select></div>
<div class="mb-2" id="wrap-other_specialty" style="display:none"><label class="form-label">Other Specialty <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-other_specialty" name="other_specialty" disabled></div>
<div class="mb-2"><label class="form-label">Doctor Location <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-doctor_location" name="doctor_location" data-gate required></div>
<div class="mb-2"><label class="form-label">Doctor Pincode <span class="text-danger">*</span></label><input type="text" class="form-control" id="field-doctor_pincode" name="doctor_pincode" inputmode="numeric" maxlength="6" data-gate required></div>
<div class="mb-2"><label class="form-label">Doctor Type <span class="text-danger">*</span></label><select class="form-control" id="field-doctor_type" name="doctor_type" data-gate required><option value="">-- Select --</option>@foreach($doctorTypes as $item)<option value="{{$item}}">{{$item}}</option>@endforeach</select></div>
<div class="mb-2"><label class="form-label">No. of Total Patients Seen in a Day <span class="text-danger">*</span></label><select class="form-control" name="total_patients" data-gate required><option value="">-- Select --</option>@foreach($bands as $item)<option value="{{$item}}">{{$item}}</option>@endforeach</select></div>
<div class="mb-2"><label class="form-label">No. of Respiratory Patients Seen in a Day <span class="text-danger">*</span></label><select class="form-control" name="respiratory_patients" data-gate required><option value="">-- Select --</option>@foreach($bands as $item)<option value="{{$item}}">{{$item}}</option>@endforeach</select></div>
<div class="mb-2"><label class="form-label" for="field-doctor_manager_joint_visit">Manager (EBM) Joint Visit Status <span class="text-danger">*</span></label><select class="form-control" id="field-doctor_manager_joint_visit" name="manager_joint_visit" data-gate required><option value="">-- Select --</option><option value="Yes">Yes</option><option value="No">No</option></select></div>
</div>
</div>
<div class="survey-section locked">
<button type="button" class="section-head">Chemist details</button>
<div class="section-body">
<div class="mb-2"><label class="form-label">Attached Chemist Name (If any)</label><input type="text" class="form-control" name="attached_chemist_name"></div>
<div class="mb-2"><label class="form-label">No. of Staff</label><input type="number" min="0" step="1" class="form-control" name="no_of_staff"></div>
</div>
</div>
<div class="survey-section locked">
<button type="button" class="section-head">Is the Doctor Prescriber of our Products?</button>
<div class="section-body">
@foreach($products as $label => $code)
<div class="mb-2"><label class="form-label">{{$label}} <span class="text-danger">*</span></label><select class="form-control" name="{{$code}}" data-gate required><option value="">-- Select --</option><option value="Yes">Yes</option><option value="No">No</option></select></div>
@endforeach
</div>
</div>
<div class="survey-section locked">
<button type="button" class="section-head">Upload photos</button>
<div class="section-body">
<p class="text-muted">Upload one of visiting card, Rx pad or doctor board. JPG, JPEG or PNG, max 10 MB.</p>
<div class="mb-2"><label class="form-label">Visiting Card</label><input type="file" class="form-control" id="field-visiting_card" name="visiting_card" accept=".jpg,.jpeg,.png,image/jpeg,image/png"></div>
<div class="mb-2"><label class="form-label">Rx Pad</label><input type="file" class="form-control" id="field-rx_pad" name="rx_pad" accept=".jpg,.jpeg,.png,image/jpeg,image/png"></div>
<div class="mb-2"><label class="form-label">Doctor Board</label><input type="file" class="form-control" id="field-doctor_board" name="doctor_board" accept=".jpg,.jpeg,.png,image/jpeg,image/png"></div>
</div>
</div>

View File

@ -0,0 +1,36 @@
@php
$orgTypes = $orgTypes ?? [];
@endphp
<p class="small fw-bold mb-1">All employees</p>
<div class="table-responsive">
<table class="table table-sm table-bordered mb-2 incept-neb-table">
<thead>
<tr>
<th></th>
@foreach(['robust', 'mini', 'control_d'] as $typeKey)
<th>{{ $orgTypes[$typeKey]['label'] ?? ucwords(str_replace('_', ' ', $typeKey)) }}</th>
@endforeach
</tr>
</thead>
<tbody>
<tr>
<th>Allocation</th>
@foreach(['robust', 'mini', 'control_d'] as $typeKey)
<td>{{ $orgTypes[$typeKey]['allocated'] ?? 0 }}</td>
@endforeach
</tr>
<tr>
<th>Submitted</th>
@foreach(['robust', 'mini', 'control_d'] as $typeKey)
<td>{{ $orgTypes[$typeKey]['used'] ?? 0 }}</td>
@endforeach
</tr>
<tr>
<th>Balance</th>
@foreach(['robust', 'mini', 'control_d'] as $typeKey)
<td>{{ $orgTypes[$typeKey]['balance'] ?? 0 }}</td>
@endforeach
</tr>
</tbody>
</table>
</div>

View File

@ -69,7 +69,7 @@
<th>PCODE </th>
<th>Speciality</th>
<th>Month of Engagement</th>
<th>Core Value</th>
<!-- <th>Core Value</th> -->
<th>Activity</th>
<th>Executed as per Plan</th>
<th>No. of Flutter Utilized</th>
@ -89,17 +89,24 @@
@endphp
<tr>
<td>
@if(isset($sub->formdata->execution_done) && $sub->formdata->execution_done == 'yes' ) Execution Done
@else
<button type="button" class="btn btn-xs btn-info edit-btn"
plan_details="{{ json_encode($sub->formdata) }}"><i
class="mdi mdi-pencil"></i> Execute</button>
@if(isset($sub->formdata->execution_done) && $sub->formdata->execution_done == 'yes')
Execution Done
@elseif(isset($sub->workflow_status) && $sub->workflow_status == 'approved')
<button type="button" class="btn btn-xs btn-info edit-btn"
plan_details="{{ json_encode($sub->formdata) }}">
<i class="mdi mdi-pencil"></i> Execute
</button>
@elseif(isset($sub->workflow_status) && $sub->workflow_status == 'rejected')
<button type="button" class="btn btn-xs btn-info edit_plan-btn"
plan_details="{{ json_encode($sub->formdata) }}"><i
class="mdi mdi-pencil"></i> Edit Plan</button>
plan_details="{{ json_encode($sub->formdata) }}">
<i class="mdi mdi-pencil"></i> Edit Plan
</button>
@endif
</td>
@ -154,10 +161,21 @@
</div>
<div id="form-wrapper">
<div class="alert alert-info mb-3">
<strong>Please ensure to add Plan for all your Core Drs which will be executed during the period Oct 2026 - Dec 2026</strong>
</div>
<form method="POST" action="/form/submit" id="report-form"
enctype="multipart/form-data">
<input type="hidden" name="form_id" value="{{ $form_id }}">
@csrf
<!-- Drive Count Fields -->
<input type="hidden" name="Count of Plans_LHC" id="count-plans-lhc" value="0">
<input type="hidden" name="Count of Execution_LHC" id="count-execution-lhc" value="0">
<input type="hidden" name="Count of Plans_Novel" id="count-plans-novel" value="0">
<input type="hidden" name="Count of Execution_Novel" id="count-execution-novel" value="0">
@if ($id)
<input type="hidden" name="id" value="{{ $id }}">
@endif
@ -171,6 +189,7 @@
'sync',
'cip',
'total',
'no_of_patients_screened',
]))
@continue
@endif
@ -273,7 +292,24 @@
enctype="multipart/form-data">
<input type="hidden" name="form_id" value="{{ $form_id }}">
@csrf
<input type="hidden" name="id" value="" id="execution-id">
<input type="hidden" name="execution_mode" id="execution-mode" value="no">
<!-- Drive Count Fields -->
<input type="hidden" name="Count of Plans_LHC"
id="execution-count-plans-lhc" value="0">
<input type="hidden" name="Count of Execution_LHC"
id="execution-count-execution-lhc" value="0">
<input type="hidden" name="Count of Plans_Novel"
id="execution-count-plans-novel" value="0">
<input type="hidden" name="Count of Execution_Novel"
id="execution-count-execution-novel" value="0">
{{-- <input type="hidden" name="execution_done" value="yes"> --}}
@foreach ($questions as $question)
<div class="col-md-12 mb-2">
@ -405,6 +441,70 @@
$('#form-wrapper').hide()
$('#execution-form-wrapper').hide()
// Patients Screened should only appear in Execution view for LHC
$('#execution-no_of_patients_screened').closest('.col-md-12').hide();
$('#execution-no_of_patients_screened').prop('required', false);
// =====================================================
// DRIVE COUNT FUNCTIONS
// =====================================================
// Plan Filling
function setPlanDriveCount() {
const activity = ($('#field-activity').val() || '').trim().toUpperCase();
$('#drive-count-plans-lhc').val('0');
$('#drive-count-execution-lhc').val('0');
$('#drive-count-plans-novel').val('0');
$('#drive-count-execution-novel').val('0');
if (activity === 'LHC') {
$('#drive-count-plans-lhc').val('1');
} else if (activity === 'NOVEL') {
$('#drive-count-plans-novel').val('1');
}
}
// Execution
function setExecutionDriveCount() {
const activity = ($('#execution-activity').val() || '').trim().toUpperCase();
$('#execution-drive-count-plans-lhc').val('0');
$('#execution-drive-count-execution-lhc').val('0');
$('#execution-drive-count-plans-novel').val('0');
$('#execution-drive-count-execution-novel').val('0');
if (activity === 'LHC') {
$('#execution-drive-count-execution-lhc').val('1');
} else if (activity === 'NOVEL') {
$('#execution-drive-count-execution-novel').val('1');
}
}
// Activity change - Plan
$('#field-activity').on('change.driveCount', function() {
setPlanDriveCount();
});
// Activity change - Execution
$('#execution-activity').on('change.driveCount', function() {
setExecutionDriveCount();
});
// Initialize Plan count
setPlanDriveCount();
$('#add-camp-btn').click(function() {
@ -417,6 +517,7 @@
})
$('.edit-btn').click(function() {
$('#execution-mode').val('yes');
$('#execution-form-wrapper').show(100, () => {
$('#add-camp-btn').hide(0);
})
@ -474,6 +575,7 @@
// if(selected[k] != '')
// $('#execution-'+k).val(selected[k]);
}
let docid = $('#execution-contact_id option').map(function() {
if ($(this).text().trim() == selected['contact_id'] || $(this).val().trim() == selected['cont_id']) return this;
@ -482,7 +584,58 @@
$('#execution-contact_id').val(docid)
$('#execution-id').val(selected['id'])
$('#execution-no_of_flutter_utilized').closest('.col-md-12').after('<h4>Prescription Generated</h4>');
// Set execution drive count
setExecutionDriveCount();
// Show Patients Screened only for LHC during Execution
const selectedActivity = ($('#execution-activity').val() || '').trim().toUpperCase();
if (selectedActivity === 'LHC') {
$('#execution-no_of_patients_screened')
.closest('.col-md-12')
.show();
$('#execution-no_of_patients_screened')
.prop('required', true);
} else {
$('#execution-no_of_patients_screened')
.closest('.col-md-12')
.hide();
$('#execution-no_of_patients_screened')
.prop('required', false)
.val('');
}
$('#execution-activity').off('change.patientsScreened').on('change.patientsScreened', function () {
const activity = ($(this).val() || '').trim().toUpperCase();
if (activity === 'LHC') {
$('#execution-no_of_patients_screened')
.closest('.col-md-12')
.show();
$('#execution-no_of_patients_screened')
.prop('required', true);
} else {
$('#execution-no_of_patients_screened')
.closest('.col-md-12')
.hide();
$('#execution-no_of_patients_screened')
.prop('required', false)
.val('');
}
});
$('#execution-no_of_patients_screened')
.closest('.col-md-12')
.after('<h4>Prescription Generated</h4>');
$('#execution-dpi,#execution-mdi,#execution-sync,#execution-cip').on('input', function() {
var total=(
@ -520,6 +673,13 @@
$('#add-camp-btn').hide(0);
})
$('#table-wrapper').hide()
// Restrict Edit Plan date selection to Oct 1, 2026 - Dec 31, 2026
$('#execution-plan_date').attr('min', '2026-10-01');
$('#execution-plan_date').attr('max', '2026-12-31');
// Edit Plan: Patients Screened is NOT applicable
$('#execution-no_of_patients_screened').closest('.col-md-12').hide();
$('#execution-no_of_patients_screened').prop('required', false);
@ -531,8 +691,8 @@
console.log("Selected Doctor:", selected['contact_id']);
$('#execution-execution_as_per_plan,#execution-execution_date,#execution-no_of_flutter_utilized,#execution-dpi,#execution-mdi,#execution-sync,#execution-cip,#execution-total').parent().hide();
$('#execution-execution_as_per_plan,#execution-execution_date,#execution-no_of_flutter_utilized,#execution-dpi,#execution-mdi,#execution-sync,#execution-cip,#execution-total').prop('required',false);
$('#execution-execution_as_per_plan,#execution-execution_date,#execution-no_of_flutter_utilized,#execution-dpi,#execution-mdi,#execution-sync,#execution-cip,#execution-total,#execution-no_of_patients_screened').parent().hide();
$('#execution-execution_as_per_plan,#execution-execution_date,#execution-no_of_flutter_utilized,#execution-dpi,#execution-mdi,#execution-sync,#execution-cip,#execution-total,#execution-no_of_patients_screened').prop('required',false);
@ -554,6 +714,14 @@
// $('#execution-'+k).val(selected[k]);
}
$('#execution-activity').on('change.driveCount', function() {
setExecutionDriveCount();
});
let docid = $('#execution-contact_id option').map(function() {
if ($(this).text().trim() == selected['contact_id'] || $(this).val().trim() == selected['cont_id']) return this;
}).attr('value')
@ -596,9 +764,17 @@
$('#execution-core_value').parent().hide();
$('#execution-core_value').prop('required',false);
$('#execution-no_of_patients_screened').on('input', function () {
let value = $(this).val();
// Remove everything except digits
value = value.replace(/[^0-9]/g, '');
$(this).val(value);
});
@ -650,35 +826,67 @@
})
$('#report-form').on('submit', function(event) {
event.preventDefault();
$("#pageloader").fadeIn();
$.ajax({
type: "POST",
url: '/form/ajaxsubmit',
data: new FormData($('#report-form')[0]),
processData: false,
contentType: false,
success: function(data) {
$("#pageloader").fadeOut();
if (data.status == 'success') {
Swal.fire({
title: 'Great!',
text: 'Your data is successfully submitted!',
icon: 'success'
})
window.location.reload();
//$('#report-form').trigger("reset");
} else {
Swal.fire({
icon: 'error',
title: 'Data not submitted',
text: 'Something went wrong, please try again after some time!'
})
}
},
});
});
// Get the currently selected activity
const activity = ($('#field-activity').val() || '').trim().toUpperCase();
// Reset all Plan/Execution counters
$('#count-plans-lhc').val('0');
$('#count-execution-lhc').val('0');
$('#count-plans-novel').val('0');
$('#count-execution-novel').val('0');
// Set Plan counter based on selected Activity
if (activity === 'LHC') {
$('#count-plans-lhc').val('1');
} else if (activity === 'NOVEL') {
$('#count-plans-novel').val('1');
}
// Debug - check before submitting
console.log('Plan Activity:', activity);
console.log('Count of Plans_LHC:', $('#count-plans-lhc').val());
console.log('Count of Plans_Novel:', $('#count-plans-novel').val());
event.preventDefault();
$("#pageloader").fadeIn();
$.ajax({
type: "POST",
url: '/form/ajaxsubmit',
data: new FormData($('#report-form')[0]),
processData: false,
contentType: false,
success: function(data) {
$("#pageloader").fadeOut();
if (data.status == 'success') {
Swal.fire({
title: 'Great!',
text: 'Your data is successfully submitted!',
icon: 'success'
});
window.location.reload();
} else {
Swal.fire({
icon: 'error',
title: 'Data not submitted',
text: 'Something went wrong, please try again after some time!'
});
}
}
});
});
$('#field-core_value').parent().hide();
@ -701,6 +909,8 @@
$('#field-core_value').val($('option:selected', this).attr('core_value'))
})
$('#field-msl_code').prop('readonly', true);
$('#field-speciality').prop('readonly', true);
@ -746,36 +956,135 @@
});
$('#execution-form').on('submit', function(event) {
event.preventDefault();
$("#pageloader").fadeIn();
$.ajax({
type: "POST",
url: '/form/ajaxsubmit',
data: new FormData($('#execution-form')[0]),
processData: false,
contentType: false,
success: function(data) {
$("#pageloader").fadeOut();
if (data.status == 'success') {
Swal.fire({
title: 'Great!',
text: 'Your data is successfully submitted!',
icon: 'success'
}).then(() => {
location.reload(); // 🔥 page refresh here
});
$('#report-form').trigger("reset");
} else {
Swal.fire({
icon: 'error',
title: 'Data not submitted',
text: 'Something went wrong, please try again after some time!'
})
}
},
});
const activity = ($('#execution-activity').val() || '')
.trim()
.toUpperCase();
const executionMode = $('#execution-mode').val();
/*
* Reset all counters first
*/
$('#execution-count-plans-lhc').val('0');
$('#execution-count-execution-lhc').val('0');
$('#execution-count-plans-novel').val('0');
$('#execution-count-execution-novel').val('0');
/*
* Only set Execution counters when
* this is actual Execution, NOT Edit Plan.
*/
if (executionMode === 'yes') {
if (activity === 'LHC') {
$('#execution-count-execution-lhc').val('1');
} else if (activity === 'NOVEL') {
$('#execution-count-execution-novel').val('1');
}
}
/*
* Patients Screened validation
*/
if (activity === 'LHC') {
const patientsScreened =
$('#execution-no_of_patients_screened').val();
if (
patientsScreened === '' ||
!/^\d+$/.test(patientsScreened)
) {
event.preventDefault();
Swal.fire({
icon: 'error',
title: 'Invalid No. of Patients Screened',
text: 'Please enter a valid whole number. Negative numbers and decimal values are not allowed.'
});
$('#execution-no_of_patients_screened').focus();
return false;
}
}
/*
* Debug before submission
*/
console.log('Execution Mode:', executionMode);
console.log('Execution Activity:', activity);
console.log(
'Count of Execution_LHC:',
$('#execution-count-execution-lhc').val()
);
console.log(
'Count of Execution_Novel:',
$('#execution-count-execution-novel').val()
);
/*
* Submit
*/
event.preventDefault();
$("#pageloader").fadeIn();
$.ajax({
type: "POST",
url: '/form/ajaxsubmit',
data: new FormData($('#execution-form')[0]),
processData: false,
contentType: false,
success: function(data) {
$("#pageloader").fadeOut();
if (data.status == 'success') {
Swal.fire({
title: 'Great!',
text: 'Your data is successfully submitted!',
icon: 'success'
}).then(() => {
location.reload();
});
} else {
Swal.fire({
icon: 'error',
title: 'Data not submitted',
text: 'Something went wrong, please try again after some time!'
});
}
},
error: function() {
$("#pageloader").fadeOut();
Swal.fire({
icon: 'error',
title: 'Data not submitted',
text: 'Something went wrong, please try again after some time!'
});
}
});
});
var today = new Date();
var dd = String(today.getDate()).padStart(2, '0');
var mm = String(today.getMonth() + 1).padStart(2, '0');

View File

@ -5,7 +5,10 @@
$hierCount = count($selectedHier);
$brandCount = count($selectedBrands);
@endphp
<form id="iva-filter-form" class="iva-filter-form" method="get" action="">
<form id="iva-filter-form" class="iva-filter-form" method="get" action="{{ $formAction ?? '' }}">
@if (!empty($token))
<input type="hidden" name="authorization" value="{{ $token }}">
@endif
<div class="d-flex flex-wrap align-items-center justify-content-end gap-2">
<div class="dropdown">
<button class="btn btn-outline-secondary dropdown-toggle form-select" type="button" id="ivaHierDropdown"

View File

@ -0,0 +1,84 @@
<style type="text/css">
body .account-pages > .container {
max-width: 100%;
padding-left: 8px;
padding-right: 8px;
}
.iva-mobile .page-title {
font-size: 1.15rem;
margin-bottom: 8px;
}
.iva-mobile .row > [class*="col-"] {
flex: 0 0 100%;
max-width: 100%;
}
.iva-mobile .iva-mobile-back {
display: inline-block;
margin-bottom: 6px;
font-size: 14px;
}
.iva-mobile .iva-filter-form,
.iva-mobile .iva-filter-form > .d-flex {
width: 100%;
justify-content: stretch !important;
}
.iva-mobile .iva-filter-form > .d-flex {
flex-direction: column;
align-items: stretch !important;
}
.iva-mobile .iva-filter-form .dropdown,
.iva-mobile .iva-filter-form .dropdown-toggle,
.iva-mobile .iva-filter-form .form-select,
.iva-mobile .iva-filter-form #iva-month-select {
width: 100% !important;
}
.iva-mobile .iva-filter-form .dropdown-menu {
width: 100%;
min-width: 0 !important;
}
.iva-mobile .iva-report-table {
font-size: 12px;
}
.iva-mobile .iva-report-table thead th {
min-width: 72px;
}
.iva-mobile .card-header {
flex-wrap: wrap;
gap: 8px;
padding-left: 12px;
padding-right: 12px;
}
.iva-mobile .dataTables_wrapper {
width: 100%;
overflow-x: auto;
}
.iva-mobile-landing {
padding: 16px 12px 24px;
}
.iva-mobile-landing .iva-mobile-actions {
display: flex;
flex-direction: column;
gap: 12px;
}
.iva-mobile-landing .iva-mobile-btn {
display: block;
width: 100%;
padding: 16px 18px;
font-size: 16px;
text-align: left;
}
</style>

View File

@ -1,14 +1,20 @@
@extends('layouts.vertical', ['page_title' => $title])
@extends(($isMobile ?? false) ? 'layouts.vertical-individual' : 'layouts.vertical', ['page_title' => $title])
@section('css')
@if (!empty($isMobile))
<meta name="viewport" content="width=device-width, initial-scale=1">
@endif
<link href="{{ asset('assets/libs/datatables/datatables.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('assets/libs/select2/select2.min.css') }}" rel="stylesheet" type="text/css" />
@endsection
@section('content')
<div class="container-fluid">
<div class="container-fluid{{ !empty($isMobile) ? ' iva-mobile' : '' }}">
<div class="row">
<div class="col-md-4 p-2">
@if (!empty($isMobile))
<a href="{{ $mobileBackUrl }}" class="iva-mobile-back">&larr; Back</a>
@endif
<h4 class="page-title">{{ $title }}</h4>
</div>
<div class="col-md-8 p-2 d-flex justify-content-end align-items-center">
@ -20,7 +26,9 @@
<div class="alert alert-danger">{{ $error }}</div>
@endif
@include('ivareport._tabs')
@if (empty($isMobile))
@include('ivareport._tabs')
@endif
<div class="row">
<div class="col-12">
@ -67,6 +75,9 @@
</div>
</div>
@include('ivareport._styles')
@if (!empty($isMobile))
@include('ivareport._mobile')
@endif
@endsection
@section('script')

View File

@ -1,13 +1,19 @@
@extends('layouts.vertical', ['page_title' => $title])
@extends(($isMobile ?? false) ? 'layouts.vertical-individual' : 'layouts.vertical', ['page_title' => $title])
@section('css')
@if (!empty($isMobile))
<meta name="viewport" content="width=device-width, initial-scale=1">
@endif
<link href="{{ asset('assets/libs/select2/select2.min.css') }}" rel="stylesheet" type="text/css" />
@endsection
@section('content')
<div class="container-fluid">
<div class="container-fluid{{ !empty($isMobile) ? ' iva-mobile' : '' }}">
<div class="row">
<div class="col-md-4 p-2">
@if (!empty($isMobile))
<a href="{{ $mobileBackUrl }}" class="iva-mobile-back">&larr; Back</a>
@endif
<h4 class="page-title">{{ $title }}</h4>
</div>
<div class="col-md-8 p-2 d-flex justify-content-end align-items-center">
@ -19,7 +25,9 @@
<div class="alert alert-danger">{{ $error }}</div>
@endif
@include('ivareport._tabs')
@if (empty($isMobile))
@include('ivareport._tabs')
@endif
<div class="row">
<div class="col-12">
@ -68,6 +76,9 @@
</div>
</div>
@include('ivareport._styles')
@if (!empty($isMobile))
@include('ivareport._mobile')
@endif
@endsection
@section('script')

View File

@ -0,0 +1,17 @@
@extends('layouts.vertical-individual', ['page_title' => $title])
@section('css')
<meta name="viewport" content="width=device-width, initial-scale=1">
@endsection
@section('content')
<div class="container-fluid iva-mobile iva-mobile-landing">
<h4 class="page-title">{{ $title }}</h4>
<div class="iva-mobile-actions">
@foreach ($reports as $report)
<a class="btn btn-primary iva-mobile-btn" href="{{ $report['url'] }}">{{ $report['label'] }}</a>
@endforeach
</div>
</div>
@include('ivareport._mobile')
@endsection

View File

@ -1,13 +1,19 @@
@extends('layouts.vertical', ['page_title' => $title])
@extends(($isMobile ?? false) ? 'layouts.vertical-individual' : 'layouts.vertical', ['page_title' => $title])
@section('css')
@if (!empty($isMobile))
<meta name="viewport" content="width=device-width, initial-scale=1">
@endif
<link href="{{ asset('assets/libs/select2/select2.min.css') }}" rel="stylesheet" type="text/css" />
@endsection
@section('content')
<div class="container-fluid">
<div class="container-fluid{{ !empty($isMobile) ? ' iva-mobile' : '' }}">
<div class="row">
<div class="col-md-4 p-2">
@if (!empty($isMobile))
<a href="{{ $mobileBackUrl }}" class="iva-mobile-back">&larr; Back</a>
@endif
<h4 class="page-title">{{ $title }}</h4>
</div>
<div class="col-md-8 p-2 d-flex justify-content-end align-items-center">
@ -19,7 +25,9 @@
<div class="alert alert-danger">{{ $error }}</div>
@endif
@include('ivareport._tabs')
@if (empty($isMobile))
@include('ivareport._tabs')
@endif
<div class="row">
<div class="col-12">
@ -84,6 +92,9 @@
</div>
</div>
@include('ivareport._styles')
@if (!empty($isMobile))
@include('ivareport._mobile')
@endif
@endsection
@section('script')

View File

@ -1,14 +1,20 @@
@extends('layouts.vertical', ['page_title' => $title])
@extends(($isMobile ?? false) ? 'layouts.vertical-individual' : 'layouts.vertical', ['page_title' => $title])
@section('css')
@if (!empty($isMobile))
<meta name="viewport" content="width=device-width, initial-scale=1">
@endif
<link href="{{ asset('assets/libs/datatables/datatables.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('assets/libs/select2/select2.min.css') }}" rel="stylesheet" type="text/css" />
@endsection
@section('content')
<div class="container-fluid">
<div class="container-fluid{{ !empty($isMobile) ? ' iva-mobile' : '' }}">
<div class="row">
<div class="col-md-4 p-2">
@if (!empty($isMobile))
<a href="{{ $mobileBackUrl }}" class="iva-mobile-back">&larr; Back</a>
@endif
<h4 class="page-title">{{ $title }}</h4>
</div>
<div class="col-md-8 p-2 d-flex justify-content-end align-items-center">
@ -20,7 +26,9 @@
<div class="alert alert-danger">{{ $error }}</div>
@endif
@include('ivareport._tabs')
@if (empty($isMobile))
@include('ivareport._tabs')
@endif
<div class="row">
<div class="col-12">
@ -82,6 +90,9 @@
</div>
</div>
@include('ivareport._styles')
@if (!empty($isMobile))
@include('ivareport._mobile')
@endif
@endsection
@section('script')

View File

@ -189,6 +189,16 @@
{{-- @endif --}}
@endif
@if(\App\Helpers\ZenovaReportAccess::canView())
<li>
<a href="/zenovareport/dashboard">
<i data-feather="file-text"></i>
<span>
Glipiq &amp; Sitazit </span>
</a>
</li>
@endif
@if(auth()->user()->domain == 'iva')
<li>
<a href="#sidebarIvaDashboards" data-bs-toggle="collapse">
@ -290,7 +300,8 @@
@if(in_array('magazine_get_all', $permissions) ||
in_array('wallmessage_get_all', $permissions) ||
in_array('wallbanner_get_all', $permissions) ||
in_array('widgets_get_all', $permissions))
in_array('widgets_get_all', $permissions) ||
in_array('action_sheet_get_all', $permissions))
<li>
<a href="#sidebarContent" data-bs-toggle="collapse">
<i data-feather="book-open"></i>
@ -324,6 +335,11 @@
<a href="/forums">Forums</a>
</li>
@endif
@if(in_array('action_sheet_get_all', $permissions))
<li>
<a href="/actionsheets">Action Sheets</a>
</li>
@endif
</ul>
</div>
</li>
@ -668,9 +684,9 @@
@if(in_array('formentity_get_all', $permissions) ||
in_array('formaction_get_all', $permissions) ||
in_array('all_data_access', $permissions) ||
in_array('formproduct_get_all', $permissions) ||
in_array('formexports_get_all', $permissions) ||
in_array('quiz_get_all', $permissions) ||
{{-- in_array('formproduct_get_all', $permissions) || --}}
{{-- in_array('quiz_get_all', $permissions) || --}}
in_array('roles_leaderboard_get_all', $permissions) ||
in_array('all_data_access', $permissions) )
@ -692,7 +708,7 @@
<a href="/formhooks">Form Hook</a>
</li>
@endif
@if(in_array('survey_get_all', $permissions))
{{-- @if(in_array('survey_get_all', $permissions))
<li>
<a href="/surveys">Survey</a>
</li>
@ -701,7 +717,7 @@
<li>
<a href="/quizs">Quiz</a>
</li>
@endif
@endif --}}
@if(in_array('formaction_get_all', $permissions))
<li>
@ -728,11 +744,11 @@
<a href="/leaderboards">Leaderboard</a>
</li>
@endif
@if(in_array('formproduct_get_all', $permissions))
{{-- @if(in_array('formproduct_get_all', $permissions))
<li>
<a href="/formproducts">Products</a>
</li>
@endif
@endif --}}
@if(auth()->user()->domain == 'titans' && in_array('all_data_access', $permissions))
<li>
<a href="/titanshcpgatheringreports">Titans HCP Data Upload</a>

View File

@ -123,9 +123,11 @@
<thead class="table-light">
<tr>
<th>Rank</th>
<th>User</th>
<th>Employee Code</th>
<th>Designation / Position</th>
<th>{{ !empty($is_team) ? 'Team' : 'User' }}</th>
@if(empty($is_team))
<th>Employee Code</th>
<th>Designation / Position</th>
@endif
<th>Score</th>
@foreach($metrics as $metric)
<th>{{ $metric['name'] }}</th>
@ -144,13 +146,15 @@
<span>{{ $user['name'] }}</span>
</div>
</td>
<td>{{ $user['employee_code'] }}</td>
<td>
{{ $user['designation'] }}
@if(!empty($user['position_name']))
<br><small class="text-muted">{{ $user['position_name'] }}</small>
@endif
</td>
@if(empty($is_team))
<td>{{ $user['employee_code'] }}</td>
<td>
{{ $user['designation'] }}
@if(!empty($user['position_name']))
<br><small class="text-muted">{{ $user['position_name'] }}</small>
@endif
</td>
@endif
<td>{{ $user['score'] }}</td>
@foreach($metrics as $metric)
<td>{{ $user['metric_values'][$metric['id']] ?? '—' }}</td>

View File

@ -62,20 +62,30 @@
<input type="text" id="field-{{$field}}" name="{{$field}}" class="form-control datepicker" placeholder="Enter {{$props['title']}}" value="{{isset($item->{$field})?$item->{$field}:''}}" {{isset($props['required'])&&$props['required']?'required':'' }}>
@break
@case('select')
@php
$__selected = (isset($item->{$field}) && $item->{$field} !== '' && $item->{$field} !== null)
? $item->{$field}
: ($props['default'] ?? null);
@endphp
<select class="form-control" id="field-{{$field}}" name="{{$field}}" {{isset($props['required'])&&$props['required']?'required':'' }}>
@if($field !== 'default_interval')
<option value=""> -- Select {{$props['title']}} -- </option>
@endif
@foreach($props['values'] as $k => $v)
<option value="{{$k}}" {{ isset($item->{$field}) && (string)$item->{$field} === (string)$k ? 'selected="selected"':''}}>{{$v}}</option>
<option value="{{$k}}" {{ (string)$__selected === (string)$k ? 'selected="selected"':''}}>{{$v}}</option>
@endforeach
</select>
@break
@case('multiselect')
@php
$__selectedList = (isset($item->{$field}) && is_array($item->{$field}) && count($item->{$field}))
? $item->{$field}
: ($props['default'] ?? []);
@endphp
<select class="form-control select2-multiple" data-toggle="select2" data-width="100%" multiple="multiple" data-placeholder="Choose {{$props['title']}}" id="field-{{$field}}" name="{{$field}}[]" {{isset($props['required'])&&$props['required']?'required':'' }}>
<option value=""> -- Select {{$props['title']}} -- </option>
@foreach($props['values'] as $k => $v)
<option value="{{$k}}" {{isset($item->{$field}) && is_array($item->{$field}) && in_array($k, $item->{$field}) ? 'selected="selected"':''}}>{{$v}}</option>
<option value="{{$k}}" {{is_array($__selectedList) && in_array($k, $__selectedList) ? 'selected="selected"':''}}>{{$v}}</option>
@endforeach
</select>
@break
@ -285,7 +295,7 @@
</div>
<style type="text/css">
.ms-container {max-width: 100%}
#wrap-leaderboard_team_ids { display: none; }
#wrap-leaderboard_team_ids, #wrap-view_mode { display: none; }
</style>
@endsection
@ -375,8 +385,10 @@
var type = $('#field-leaderboard_roles_type').val() || 'roles';
var isTeam = type === 'team';
$('#wrap-leaderboard_team_ids').toggle(isTeam);
$('#wrap-view_mode').toggle(isTeam);
$('#wrap-rank_roles').toggle(!isTeam);
setWrapRequired($('#wrap-leaderboard_team_ids'), isTeam);
setWrapRequired($('#wrap-view_mode'), isTeam);
setWrapRequired($('#wrap-rank_roles'), !isTeam);
if (isTeam) {
initTeamsSelect2();
@ -385,6 +397,13 @@
$('#field-leaderboard_roles_type').on('change', toggleLeaderboardTypeFields);
toggleLeaderboardTypeFields();
function toggleUserAttributesField() {
var show = $('#field-show_user_attributes').val() === 'yes';
$('#field-user_attributes').closest('.mb-3').toggle(show);
}
toggleUserAttributesField();
$('#field-show_user_attributes').on('change', toggleUserAttributesField);
function bindRemoveButtons() {
$('.btn-remove').off('click').on('click', function() {
$(this).closest('tr').remove();

View File

@ -57,18 +57,28 @@
<textarea id="field-{{$field}}" name="{{$field}}" class="form-control" placeholder="Enter {{$props['title']}}" {{isset($props['required'])&&$props['required']?'required':'' }}>{{$mode=='edit'&&isset($item->{$field})?$item->{$field}:''}}</textarea>
@break
@case('select')
@php
$__selected = (isset($item->{$field}) && $item->{$field} !== '' && $item->{$field} !== null)
? $item->{$field}
: ($props['default'] ?? null);
@endphp
<select class="form-control" id="field-{{$field}}" name="{{$field}}" {{isset($props['required'])&&$props['required']?'required':'' }}>
<option value=""> -- Select {{$props['title']}} -- </option>
@foreach($props['values'] as $k => $v)
<option value="{{$k}}" {{$mode=='edit' && isset($item->{$field}) && $item->{$field} == $k ? 'selected="selected"':''}}>{{$v}}</option>
<option value="{{$k}}" {{ (string)$__selected === (string)$k ? 'selected="selected"':''}}>{{$v}}</option>
@endforeach
</select>
@break
@case('multiselect')
@php
$__selectedList = (isset($item->{$field}) && is_array($item->{$field}) && count($item->{$field}))
? $item->{$field}
: ($props['default'] ?? []);
@endphp
<select class="form-control select2-multiple" data-toggle="select2" data-width="100%" multiple="multiple" data-placeholder="Choose {{$props['title']}}" id="field-{{$field}}" name="{{$field}}[]" {{isset($props['required'])&&$props['required']?'required':'' }}>
<option value=""> -- Select {{$props['title']}} -- </option>
@foreach($props['values'] as $k => $v)
<option value="{{$k}}" {{$mode=='edit' && isset($item->{$field}) && is_array($item->{$field}) && in_array($k, $item->{$field}) ? 'selected="selected"':''}}>{{$v}}</option>
<option value="{{$k}}" {{is_array($__selectedList) && in_array($k, $__selectedList) ? 'selected="selected"':''}}>{{$v}}</option>
@endforeach
</select>
@break
@ -448,6 +458,12 @@
<script type="text/javascript">
$(document).ready(function(){
$('[data-toggle="select2"]').select2();
function toggleUserAttributesField() {
var show = $('#field-show_user_attributes').val() === 'yes';
$('#field-user_attributes').closest('.mb-3').toggle(show);
}
toggleUserAttributesField();
$('#field-show_user_attributes').on('change', toggleUserAttributesField);
$('.dropify').dropify({
messages: {
'default': 'Drag and drop a file here or click',

View File

@ -82,12 +82,17 @@
@break
@case('select')
@php
$__selected = (isset($item->{$field}) && $item->{$field} !== '' && $item->{$field} !== null)
? $item->{$field}
: ($props['default'] ?? null);
@endphp
<select class="form-control" id="field-{{ $field }}" name="{{ $field }}"
{{ isset($props['required']) && $props['required'] ? 'required' : '' }}>
<option value=""> -- Select {{ $props['title'] }} -- </option>
@foreach ($props['values'] as $k => $v)
<option value="{{ $k }}"
{{ $mode == 'edit' && isset($item->{$field}) && $item->{$field} == $k ? 'selected="selected"' : '' }}>
{{ (string) $__selected === (string) $k ? 'selected="selected"' : '' }}>
{{ $v }}</option>
@endforeach
</select>
@ -108,10 +113,15 @@
@break --}}
@case('multiselect')
@php
$__selectedList = (isset($item->{$field}) && is_array($item->{$field}) && count($item->{$field}))
? $item->{$field}
: ($props['default'] ?? []);
@endphp
<select class="form-control select2" data-toggle="select2" multiple data-width="100%" data-placeholder="Choose {{$props['title']}}" id="field-{{$field}}" name="{{$field}}[]" {{isset($props['required'])&&$props['required']?'required':'' }}>
{{-- <option value=""> -- Select {{$props['title']}} -- </option> --}}
@foreach($props['values'] as $k => $v)
<option value="{{$k}}" {{$mode=='edit' && isset($item->{$field}) && in_array($k, $item->{$field}) ? 'selected="selected"':''}}>{{$v}}</option>
<option value="{{$k}}" {{is_array($__selectedList) && in_array($k, $__selectedList) ? 'selected="selected"':''}}>{{$v}}</option>
@endforeach
</select>
@break
@ -615,7 +625,20 @@
<script src="{{ asset('assets/libs/multiselect/multiselect.min.js') }}"></script>
<script src="{{ URL::asset('assets/libs/flatpickr/flatpickr.min.js') }}"></script>
<script type="text/javascript">
function toggleUserAttributesField() {
var show = $('#field-show_user_attributes').val() === 'yes';
var $field = $('#field-user_attributes');
$field.closest('.mb-3').toggle(show);
if (show && $field.length) {
if ($field.hasClass('select2-hidden-accessible')) {
$field.select2('destroy');
}
$field.select2({ width: '100%' });
}
}
$(document).ready(function() {
$('#field-show_user_attributes').on('change', toggleUserAttributesField);
$('.datepicker').flatpickr({
dateFormat: "m/d/Y"
@ -801,6 +824,7 @@ $(document).ready(function() {
});
initMetricSelect2();
toggleUserAttributesField();
}

View File

@ -0,0 +1,143 @@
@extends('layouts.vertical', ['page_title' => $title])
@section('css')
<style>
.zenova-wrap {
overflow: auto;
max-height: calc(100vh - 220px);
}
.zenova-table {
border-collapse: separate;
border-spacing: 0;
font-size: 12px;
min-width: 100%;
}
.zenova-table th,
.zenova-table td {
border: 1px solid #e5e7eb;
white-space: nowrap;
padding: 6px 8px;
background: #fff;
}
.zenova-table thead th {
position: sticky;
z-index: 2;
text-align: center;
font-weight: 600;
}
.zenova-table thead tr:first-child th {
top: 0;
}
.zenova-table thead tr:nth-child(2) th {
top: 34px;
}
.zenova-table td {
text-align: right;
}
.zenova-table td.text-cell,
.zenova-table th.text-cell {
text-align: left;
}
.zenova-table tr.totals td {
font-weight: 700;
background: #f8f9fa;
}
.tone-demo { background: #fff2cc !important; }
.tone-day { background: #ddebf7 !important; }
.tone-yesterday { background: #e2efda !important; }
.tone-today { background: #c6efce !important; }
.tone-mtd { background: #fce4d6 !important; }
.tone-ytd { background: #d9e2f3 !important; }
</style>
@endsection
@section('content')
<div class="container-fluid">
<div class="row">
<div class="col-md-6 p-2">
<h4 class="page-title">{{ $title }}</h4>
</div>
<div class="col-md-6 p-2 d-flex justify-content-end align-items-center">
<form method="get" action="/zenovareport/dashboard" class="d-flex align-items-center gap-2">
<label for="level" class="mb-0">Level</label>
<select class="form-select" name="level" id="level" style="width: 110px;" onchange="this.form.submit()">
@for ($option = 1; $option <= 5; $option++)
<option value="{{ $option }}" {{ (int) $level === $option ? 'selected' : '' }}>
Level {{ $option }}
</option>
@endfor
</select>
<a href="/zenovareport/export?level={{ $level }}" class="btn btn-primary">Export</a>
</form>
</div>
</div>
@if (session()->has('error'))
<div class="alert alert-danger">{{ session()->get('error') }}</div>
@endif
@if (!empty($error))
<div class="alert alert-danger">{{ $error }}</div>
@endif
<div class="card">
<div class="card-body">
<div class="zenova-wrap">
<table class="zenova-table">
<thead>
<tr>
@foreach ($groups as $group)
<th class="tone-{{ $group['tone'] }}" colspan="{{ count($group['columns']) }}">
{{ $group['label'] }}
</th>
@endforeach
</tr>
<tr>
@foreach ($groups as $group)
@foreach ($group['columns'] as $column)
<th class="tone-{{ $group['tone'] }} {{ $column['type'] === 'text' ? 'text-cell' : '' }}">
{{ $column['label'] }}
</th>
@endforeach
@endforeach
</tr>
</thead>
<tbody>
@forelse ($body as $line)
<tr>
@php $cellIndex = 0; @endphp
@foreach ($groups as $group)
@foreach ($group['columns'] as $column)
<td class="{{ $column['type'] === 'text' ? 'text-cell' : '' }}">
{{ $line[$cellIndex] ?? '' }}
</td>
@php $cellIndex++; @endphp
@endforeach
@endforeach
</tr>
@empty
<tr>
<td colspan="{{ max(1, collect($groups)->sum(fn ($group) => count($group['columns']))) }}">
No positions found for this level.
</td>
</tr>
@endforelse
@if (!empty($totals))
<tr class="totals">
@php $cellIndex = 0; @endphp
@foreach ($groups as $group)
@foreach ($group['columns'] as $column)
<td class="{{ $column['type'] === 'text' ? 'text-cell' : '' }}">
{{ $totals[$cellIndex] ?? '' }}
</td>
@php $cellIndex++; @endphp
@endforeach
@endforeach
</tr>
@endif
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection

View File

@ -162,7 +162,7 @@ $modules = ['user' => UserController::class,
'formentity' => FormentityController::class,
'formquestion' => FormquestionController::class,
'formaction' => FormactionController::class,
'formproduct' => FormproductController::class,
// 'formproduct' => FormproductController::class,
'contactattribute' => ContactattributeController::class,
'contact' => ContactController::class,
'contactlist' => ContactlistController::class,
@ -179,9 +179,9 @@ $modules = ['user' => UserController::class,
'competition' => CompetitionController::class,
'formpersonalization' => FormpersonalizationController::class,
'survey' => SurveyController::class,
'surveyquestion' => SurveyquestionController::class,
'surveysubmission' => SurveysubmissionController::class,
// 'survey' => SurveyController::class,
// 'surveyquestion' => SurveyquestionController::class,
// 'surveysubmission' => SurveysubmissionController::class,
'campaign' => CampaignController::class,
'campaignrule' => CampaignruleController::class,
'campaignreport' => CampaignreportController::class,
@ -195,9 +195,9 @@ $modules = ['user' => UserController::class,
'commbotmessage' => CommbotmessageController::class,
'commbotintent' => CommbotintentController::class,
'quiz' => QuizController::class,
'quizquestion' => QuizquestionController::class,
'quizsubmission' => QuizsubmissionController::class,
// 'quiz' => QuizController::class,
// 'quizquestion' => QuizquestionController::class,
// 'quizsubmission' => QuizsubmissionController::class,
'course' => CourseController::class,
'lesson' => LessonController::class,
@ -290,14 +290,14 @@ Route::post('/neb_mitra/doctor/delete', [FormController::class, 'delete_doctor_a
Route::get('/form/{finfo}', [FormController::class, 'handle']);
Route::get('/srvey/{finfo}', [FormController::class, 'surveyhandle']);
Route::post('/srvey/ajaxsubmit', [FormController::class, 'ajaxsurveysubmit']);
// Route::get('/srvey/{finfo}', [FormController::class, 'surveyhandle']);
// Route::post('/srvey/ajaxsubmit', [FormController::class, 'ajaxsurveysubmit']);
Route::get('/quizly/{finfo}', [FormController::class, 'quizhandle']);
Route::post('/quizly/ajaxsubmit', [FormController::class, 'ajaxquizsubmit']);
// Route::get('/quizly/{finfo}', [FormController::class, 'quizhandle']);
// Route::post('/quizly/ajaxsubmit', [FormController::class, 'ajaxquizsubmit']);
Route::post('/formdatalist/zipmultiple', [FormController::class, 'zipmultiple']);
Route::get('/srvey/ciplatodoctor', [SurveyController::class, 'ajaxgnpsubmit']);
// Route::get('/srvey/ciplatodoctor', [SurveyController::class, 'ajaxgnpsubmit']);
Route::post('doctor/submitnew', [DoctorController::class, 'createdoctor']);

View File

@ -103,6 +103,7 @@ use App\Http\Controllers\DepotwithtmmappingController;
use App\Http\Controllers\GennovadcReportController;
use App\Http\Controllers\IvareportController;
use App\Http\Controllers\ZenovaReportController;
use App\Http\Controllers\FormhookController;
@ -119,6 +120,7 @@ use App\Http\Controllers\TitansbrandunitsreportController;
use App\Http\Controllers\MonsoonyatraactivitylistController;
use App\Http\Controllers\MetriccampaignController;
use App\Http\Controllers\LeaderboardController;
use App\Http\Controllers\ActionsheetController;
@ -195,13 +197,14 @@ $modules = ['user' => UserController::class,
'formentity' => FormentityController::class,
'formquestion' => FormquestionController::class,
'formaction' => FormactionController::class,
'formproduct' => FormproductController::class,
// 'formproduct' => FormproductController::class,
'contactattribute' => ContactattributeController::class,
'contact' => ContactController::class,
'contactexport' => ContactexportController::class,
'contactlist' => ContactlistController::class,
'reminder' => ReminderController::class,
'widget' => WidgetController::class,
'actionsheet' => ActionsheetController::class,
'formdata' => FormdataController::class,
'formsubmission' => FormsubmissionController::class,
'formexport' => FormexportController::class,
@ -213,9 +216,9 @@ $modules = ['user' => UserController::class,
'competition' => CompetitionController::class,
'formpersonalization' => FormpersonalizationController::class,
'survey' => SurveyController::class,
'surveyquestion' => SurveyquestionController::class,
'surveysubmission' => SurveysubmissionController::class,
// 'survey' => SurveyController::class,
// 'surveyquestion' => SurveyquestionController::class,
// 'surveysubmission' => SurveysubmissionController::class,
'campaign' => CampaignController::class,
'campaignrule' => CampaignruleController::class,
'campaignreport' => CampaignreportController::class,
@ -229,9 +232,9 @@ $modules = ['user' => UserController::class,
'commbotmessage' => CommbotmessageController::class,
'commbotintent' => CommbotintentController::class,
'quiz' => QuizController::class,
'quizquestion' => QuizquestionController::class,
'quizsubmission' => QuizsubmissionController::class,
// 'quiz' => QuizController::class,
// 'quizquestion' => QuizquestionController::class,
// 'quizsubmission' => QuizsubmissionController::class,
'course' => CourseController::class,
'lesson' => LessonController::class,
@ -265,6 +268,7 @@ $modules = ['user' => UserController::class,
'coachingtooldashboard' => CoachingtoolDashboard::class,
'gennovadcreport' => GennovadcReportController::class,
'zenovareport' => ZenovaReportController::class,
'ivareport' => IvareportController::class,
'formhook' => FormhookController::class,
@ -360,14 +364,14 @@ Route::get('/form/team2bfeducators', [FormController::class, 'team2bfeducators']
Route::get('/form/{finfo}', [FormController::class, 'handle']);
Route::get('/srvey/{finfo}', [FormController::class, 'surveyhandle']);
Route::post('/srvey/ajaxsubmit', [FormController::class, 'ajaxsurveysubmit']);
// Route::get('/srvey/{finfo}', [FormController::class, 'surveyhandle']);
// Route::post('/srvey/ajaxsubmit', [FormController::class, 'ajaxsurveysubmit']);
Route::get('/quizly/{finfo}', [FormController::class, 'quizhandle']);
Route::post('/quizly/ajaxsubmit', [FormController::class, 'ajaxquizsubmit']);
// Route::get('/quizly/{finfo}', [FormController::class, 'quizhandle']);
// Route::post('/quizly/ajaxsubmit', [FormController::class, 'ajaxquizsubmit']);
Route::post('/formdatalist/zipmultiple', [FormController::class, 'zipmultiple']);
Route::get('/srvey/ciplatodoctor', [SurveyController::class, 'ajaxgnpsubmit']);
// Route::get('/srvey/ciplatodoctor', [SurveyController::class, 'ajaxgnpsubmit']);
Route::post('doctor/submitnew', [DoctorController::class, 'createdoctor']);