Rapport-Laravel/resources/views/metriccampaign/form.blade.php

890 lines
55 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@extends('layouts.vertical', ["page_title"=> "Add & Edit Campaigns"])
@section('css')
<link href="{{asset('assets/libs/select2/select2.min.css')}}" rel="stylesheet" type="text/css" />
<link href="{{asset('assets/libs/multiselect/multiselect.min.css')}}" rel="stylesheet" type="text/css" />
<link href="{{ URL::asset('assets/libs/flatpickr/flatpickr.min.css')}}" rel="stylesheet" type="text/css" />
<link href="{{ URL::asset('assets/libs/dropify/dropify.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
<div class="row mt-3">
<div class="col-xl-12">
<div class="card">
<form method="post" 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')
@case('email')
@case('number')
@case('tel')
<input type="{{$props['type']}}" id="field-{{$field}}" name="{{$field}}" class="form-control" placeholder="{{ $props['placeholder'] ?? 'Enter '.$props['title'] }}" value="{{$mode=='edit'&&isset($item->{$field})?$item->{$field}:''}}" {{isset($props['required'])&&$props['required']?'required':'' }}>
@break
@case('date')
<input type="text" id="field-{{$field}}" name="{{$field}}" class="form-control datepicker" placeholder="Enter {{$props['title']}}" value="{{$mode=='edit'&&isset($item->{$field})?$item->{$field}:''}}" {{isset($props['required'])&&$props['required']?'required':'' }}>
@break
@case('datetime')
<input type="text" id="field-{{$field}}" name="{{$field}}" class="form-control datetimepicker" placeholder="Enter {{$props['title']}}" 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" 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}}" {{ (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}}" {{is_array($__selectedList) && in_array($k, $__selectedList) ? 'selected="selected"':''}}>{{$v}}</option>
@endforeach
</select>
@break
@case('image')
<div class="mt-3" id="file_wrapper">
<input type="file" name="{{$field}}" class="dropify" data-default-file="{{$mode=='edit'&&isset($item->{$field})?$item->{$field}:''}}" data-allowed-file-extensions="png jpg jpeg gif" />
<p class="text-muted text-center mt-2 mb-0">500MB Max File size</p>
</div>
@break
@case('multiimage')
<div class="mt-3" id="file_wrapper">
<input type="file" name="{{$field}}[]" multiple class="dropify" data-allowed-file-extensions="png jpg jpeg gif" />
<p class="text-muted text-center mt-2 mb-0">500MB Max File size</p>
</div>
@break
@case('file')
<div class="mt-3" id="file_wrapper">
<input type="file" name="{{$field}}" class="dropify" data-default-file="{{$mode=='edit'?$item->{$field}:''}}" data-allowed-file-extensions="png jpg jpeg gif mp4 pdf mp3 zip csv" />
<p class="text-muted text-center mt-2 mb-0">1GB Max File size</p>
</div>
@break
@case('ranking_attributes')
@php
$rankingRows = [];
if ($mode === 'edit') {
$rawRanking = $item->ranking_attributes ?? null;
if (is_string($rawRanking) && $rawRanking !== '') {
$decodedRanking = json_decode($rawRanking, true);
$rawRanking = is_array($decodedRanking) ? $decodedRanking : [];
}
if (is_object($rawRanking)) {
$rawRanking = json_decode(json_encode($rawRanking), true);
}
if (is_array($rawRanking)) {
foreach ($rawRanking as $rankRow) {
if (is_object($rankRow)) {
$rankRow = (array) $rankRow;
}
if (!is_array($rankRow)) {
continue;
}
$rowCode = trim((string) ($rankRow['code'] ?? ''));
$rowValue = array_key_exists('value', $rankRow)
? trim((string) $rankRow['value'])
: '';
if ($rowCode === '') {
continue;
}
$rankingRows[] = [
'code' => $rowCode,
'value' => $rowValue,
];
}
}
}
$rankingAttrMeta = $props['attributes_meta'] ?? [];
$optionAttrTypes = ['select', 'multiselect', 'checkbox', 'radio'];
@endphp
<div class="col-md-12">
<p class="text-muted small mb-2">Filter ranking by user attributes. Add multiple filters; each needs an attribute and value.</p>
<button type="button" class="btn btn-info btn-xs float-end" id="btn-add-ranking-attribute">Add Filter</button>
</div>
<div id="ranking-attributes-wrapper" class="col-md-12 mt-2">
@if(count($rankingRows) > 0)
<div class="row mb-2 ranking-attributes-header">
<div class="col-md-5"><label class="form-label">Attribute</label></div>
<div class="col-md-5"><label class="form-label">Value</label></div>
<div class="col-md-2"></div>
</div>
@endif
@foreach($rankingRows as $rankRow)
@php
$rowCode = $rankRow['code'];
$rowValue = $rankRow['value'];
$rowMeta = $rankingAttrMeta[$rowCode] ?? null;
$useOptions = $rowMeta
&& in_array(($rowMeta['type'] ?? ''), $optionAttrTypes, true)
&& !empty($rowMeta['options']);
@endphp
<div class="row mb-2 ranking-attribute-row">
<div class="col-md-5">
<select class="form-control ranking-attr-code" name="ranking_attribute_code[]">
<option value=""> -- Select Attribute -- </option>
@if($rowCode !== '' && !isset($props['attributes'][$rowCode]))
<option value="{{ $rowCode }}" selected>{{ $rowCode }}</option>
@endif
@foreach(($props['attributes'] ?? []) as $attrCode => $attrLabel)
<option value="{{ $attrCode }}" {{ (string)$rowCode === (string)$attrCode ? 'selected' : '' }}>{{ $attrLabel }}</option>
@endforeach
</select>
</div>
<div class="col-md-5 ranking-attr-value-wrap">
@if($useOptions)
<select class="form-control ranking-attr-value" name="ranking_attribute_value[]">
<option value=""> -- Select Value -- </option>
@if($rowValue !== '' && !isset($rowMeta['options'][$rowValue]))
<option value="{{ $rowValue }}" selected>{{ $rowValue }}</option>
@endif
@foreach($rowMeta['options'] as $optVal => $optLabel)
<option value="{{ $optVal }}" {{ (string)$rowValue === (string)$optVal ? 'selected' : '' }}>{{ $optLabel }}</option>
@endforeach
</select>
@else
<input type="text" class="form-control ranking-attr-value" name="ranking_attribute_value[]" value="{{ $rowValue }}" placeholder="Enter value">
@endif
</div>
<div class="col-md-2">
<button type="button" class="btn btn-danger waves-effect waves-light btn-remove-ranking-attribute"><i class="mdi mdi-close"></i></button>
</div>
</div>
@endforeach
</div>
@break
@case('mapping')
<div>
<button type="button" class="btn btn-info btn-xs float-end" id="btn-add-mapping">Add Metric</button>
</div>
<table class="table mb-0" id="table-metrics">
<thead>
<tr>
<th>Metric</th>
<th>Display</th>
<th>Sort</th>
<th>Weight</th>
<th>Label Text</th>
<th>Unit Type</th>
<th>Rules</th>
<th></th>
</tr>
</thead>
<tbody>
@if($mode=='edit' && isset($item->{$field}) && is_array($item->{$field}))
@foreach($item->{$field} as $map)
@php
$mapRules = is_object($map) ? ($map->rules ?? []) : ($map['rules'] ?? []);
if (is_string($mapRules)) {
$mapRules = json_decode($mapRules, true) ?: [];
} elseif (!is_array($mapRules)) {
$mapRules = json_decode(json_encode($mapRules), true) ?: [];
}
$mapRulesCount = is_array($mapRules) ? count($mapRules) : 0;
@endphp
<tr>
<td>
<select class="form-control metric-select" name="metric[]">
<option value=""> -- Select Metric -- </option>
@foreach($props['metrics'] as $k => $v)
<option value="{{$k}}" {{isset($map->metric) && $map->metric == $k ? 'selected="selected"':''}}>{{$v}}</option>
@endforeach
</select>
</td>
<td><input type="text" name="display_unit[]" class="form-control" value="{{isset($map->display)?$map->display:''}}"></td>
<td><input type="number" name="metric_sort[]" class="form-control" value="{{isset($map->sort)?$map->sort:0}}"></td>
<td><input type="number" name="weight[]" class="form-control" step="any" value="{{isset($map->weight)?$map->weight:1}}"></td>
<td><input type="text" name="label_text[]" class="form-control" value="{{isset($map->label_text)?$map->label_text:'units'}}"></td>
<td>
<select class="form-control" name="unit_type[]">
<option value="sum" {{ (!isset($map->unit_type) || $map->unit_type === 'sum') ? 'selected' : '' }}>Sum</option>
<option value="average" {{ (isset($map->unit_type) && $map->unit_type === 'average') ? 'selected' : '' }}>Average</option>
</select>
</td>
<td>
<input type="hidden" name="metric_rules[]" class="metric-rules-input" value='@json($mapRules)'>
<input type="hidden" name="metric_rules_interval[]" class="metric-rules-interval-input" value="{{ $map->rules_interval ?? 'overall' }}">
<input type="hidden" name="metric_rules_custom_start[]" class="metric-rules-custom-start-input" value="{{ $map->rules_custom_start ?? '' }}">
<input type="hidden" name="metric_rules_custom_end[]" class="metric-rules-custom-end-input" value="{{ $map->rules_custom_end ?? '' }}">
<button type="button" class="btn btn-sm {{ $mapRulesCount > 0 ? 'btn-success' : 'btn-outline-info' }} btn-metric-rules">
Rules{{ $mapRulesCount > 0 ? ' ('.$mapRulesCount.')' : '' }}
</button>
</td>
<th><button type="button" class="btn btn-danger waves-effect waves-light btn-remove"><i class="mdi mdi-close"></i></button></th>
</tr>
@endforeach
@endif
</tbody>
</table>
@break
@case('combined_metrics')
<div><button type="button" class="btn btn-info btn-xs float-end" id="btn-add-combined-metric">Add Metric</button></div>
<table class="table mb-0" id="table-combined">
<thead>
<tr>
<th width="10%">Type</th>
<th width="10%">Title</th>
<th width="28%">Include Metrics</th>
<th width="8%">Weight</th>
<th width="10%">Label Text</th>
<th width="10%">Unit Type</th>
<th width="8%">Rules</th>
<th width="5%"></th>
</tr>
</thead>
<tbody>
@php
$combinedMetricsRows = [];
if ($mode == 'edit' && isset($item->{$field}) && $item->{$field} !== '' && $item->{$field} !== '[]') {
$decoded = json_decode($item->{$field}, true);
$combinedMetricsRows = is_array($decoded) ? $decoded : [];
}
@endphp
@if(!empty($combinedMetricsRows))
@foreach($combinedMetricsRows as $i => $map)
@php
if (is_object($map)) {
$map = json_decode(json_encode($map), true);
}
if (!is_array($map)) {
$map = [];
}
$combinedRules = $map['rules'] ?? [];
if (is_string($combinedRules)) {
$combinedRules = json_decode($combinedRules, true) ?: [];
} elseif (!is_array($combinedRules)) {
$combinedRules = json_decode(json_encode($combinedRules), true) ?: [];
}
$combinedRulesCount = is_array($combinedRules) ? count($combinedRules) : 0;
$combinedRulesInterval = $map['rules_interval'] ?? 'overall';
$combinedRulesCustomStart = $map['rules_custom_start'] ?? '';
$combinedRulesCustomEnd = $map['rules_custom_end'] ?? '';
if (empty($map['rules_interval']) && !empty($combinedRules[0]['interval'])) {
$combinedRulesInterval = $combinedRules[0]['interval'];
$combinedRulesCustomStart = $combinedRules[0]['custom_start'] ?? '';
$combinedRulesCustomEnd = $combinedRules[0]['custom_end'] ?? '';
}
@endphp
<tr>
<td>
<select class="form-control" name="combined_metrics[type][]" required>
<option value=""> -- Select Type -- </option>
<option value="prescribers" {{($map['type'] ?? '') == 'prescribers' ? 'selected':''}}>Prescribers (no of)</option>
<option value="unique_new" {{($map['type'] ?? '') == 'unique_new' ? 'selected':''}}>Unique New Prescribers (launch)</option>
<option value="combination" {{($map['type'] ?? '') == 'combination' ? 'selected':''}}>Combination (Total)</option>
</select>
</td>
<td><input type="text" name="combined_metrics[title][]" class="form-control" value="{{$map['title'] ?? ''}}"></td>
<td>
<select class="form-control select2-multiple" data-toggle="select2" data-width="100%" multiple="multiple" data-placeholder="Choose Metrics" name="combined_metrics[fields][{{$i}}][]">
<option value=""> -- Select Multiple Metrics -- </option>
@foreach($fields['metrics']['metrics'] as $k => $v)
<option value="{{$k}}" {{isset($map['fields']) && is_array($map['fields']) && in_array($k, $map['fields']) ? 'selected="selected"':''}}>{{$v}}</option>
@endforeach
</select>
</td>
<td><input type="number" name="combined_metrics[weight][]" class="form-control" step="any" value="{{$map['weight'] ?? 1}}"></td>
<td><input type="text" name="combined_metrics[label_text][]" class="form-control" value="{{$map['label_text'] ?? 'units'}}"></td>
<td>
<select class="form-control" name="combined_metrics[unit_type][]">
<option value="sum" {{ (!isset($map['unit_type']) || $map['unit_type'] === 'sum') ? 'selected' : '' }}>Sum</option>
<option value="average" {{ (isset($map['unit_type']) && $map['unit_type'] === 'average') ? 'selected' : '' }}>Average</option>
</select>
</td>
<td>
<input type="hidden" name="combined_metrics[rules][]" class="metric-rules-input" value='@json($combinedRules)'>
<input type="hidden" name="combined_metrics[rules_interval][]" class="metric-rules-interval-input" value="{{ $combinedRulesInterval }}">
<input type="hidden" name="combined_metrics[rules_custom_start][]" class="metric-rules-custom-start-input" value="{{ $combinedRulesCustomStart }}">
<input type="hidden" name="combined_metrics[rules_custom_end][]" class="metric-rules-custom-end-input" value="{{ $combinedRulesCustomEnd }}">
<button type="button" class="btn btn-sm {{ $combinedRulesCount > 0 ? 'btn-success' : 'btn-outline-info' }} btn-metric-rules">
Rules{{ $combinedRulesCount > 0 ? ' ('.$combinedRulesCount.')' : '' }}
</button>
</td>
<th><button type="button" class="btn btn-danger waves-effect waves-light btn-remove-combined"><i class="mdi mdi-close"></i></button></th>
</tr>
@endforeach
@endif
</tbody>
</table>
@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>
<div class="modal fade" id="metric-rules-modal" tabindex="-1" aria-labelledby="metric-rules-modal-label" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="metric-rules-modal-label">Metric Calculation Rules</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p class="text-muted mb-3">Metric: <strong id="metric-rules-modal-metric-name"></strong></p>
<div class="card mb-3" id="rules-interval-section">
<div class="card-body py-2">
<div class="row align-items-end">
<div class="col-md-4">
<label class="form-label mb-1">Interval</label>
<select class="form-control form-control-sm" id="metric-rules-interval">
<option value="overall">Overall</option>
<option value="monthly">Monthly</option>
<option value="weekly">Weekly</option>
<option value="daily">Daily</option>
<option value="custom">Custom</option>
</select>
</div>
<div class="col-md-8" id="metric-rules-custom-dates" style="display:none;">
<div class="row">
<div class="col-6">
<label class="form-label mb-1">Start Date</label>
<input type="text" class="form-control form-control-sm" id="metric-rules-custom-start" placeholder="Start date">
</div>
<div class="col-6">
<label class="form-label mb-1">End Date</label>
<input type="text" class="form-control form-control-sm" id="metric-rules-custom-end" placeholder="End date">
</div>
</div>
</div>
</div>
</div>
</div>
@foreach(['Linear', 'Staggered', 'Milestone'] as $ruleType)
<div class="card mb-3 rule-section" data-rule-type="{{ $ruleType }}">
<div class="card-header py-2">
<div class="form-check mb-0">
<input type="checkbox" class="form-check-input rule-enabled" id="rule-enabled-{{ strtolower($ruleType) }}">
<label class="form-check-label fw-semibold" for="rule-enabled-{{ strtolower($ruleType) }}">{{ $ruleType }}</label>
</div>
</div>
<div class="card-body rule-section-body py-2">
<div class="table-responsive">
<table class="table table-sm mb-2 rule-ranges">
<thead>
<tr>
<th>Start</th>
<th>End</th>
<th>Weight</th>
<th width="50"></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<button type="button" class="btn btn-sm btn-outline-primary btn-add-range" data-rule-type="{{ $ruleType }}">Add Range</button>
@if($ruleType === 'Linear')
<small class="text-muted d-block mt-2">Linear: points = units × weight when units ≥ start</small>
@elseif($ruleType === 'Staggered')
<small class="text-muted d-block mt-2">Staggered: adds weight when start ≤ units ≤ end</small>
@else
<small class="text-muted d-block mt-2">Milestone: adds weight when units ≥ start (cumulative)</small>
@endif
</div>
</div>
@endforeach
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="btn-save-metric-rules">Save Rules</button>
</div>
</div>
</div>
</div>
@endsection
@section('script')
<script src="{{asset('assets/libs/select2/select2.min.js')}}"></script>
<script src="{{asset('assets/libs/multiselect/multiselect.min.js')}}"></script>
<script src="{{ URL::asset('assets/libs/flatpickr/flatpickr.min.js')}}"></script>
<script src="{{ URL::asset('assets/libs/dropify/dropify.min.js')}}"></script>
<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',
'replace': 'Drag and drop or click to replace',
'remove': 'Remove',
'error': 'Ooops, something wrong appended.'
},
error: {
'fileSize': 'The file size is too big (500MB max).'
}
});
$('.datepicker').flatpickr({
dateFormat: "m/d/Y"
});
$('.datetimepicker').flatpickr({
enableTime: true,
dateFormat: "m/d/Y H:i"
});
function initMetricSelect2($context) {
($context || $('#table-metrics')).find('select.metric-select').each(function() {
let $select = $(this);
if ($select.hasClass('select2-hidden-accessible')) {
$select.select2('destroy');
}
$select.select2({
width: '100%',
placeholder: '-- Select Metric --',
allowClear: true
});
});
}
initMetricSelect2();
function bindRemove() {
$('.btn-remove').off('click').on('click', function(){
$(this).closest('tr').remove();
});
}
bindRemove();
const RULE_TYPES = ['Linear', 'Staggered', 'Milestone'];
let $activeRulesRow = null;
let metricRulesModal = null;
function initRuleDatepickers() {
['#metric-rules-custom-start', '#metric-rules-custom-end'].forEach(function(selector) {
const el = document.querySelector(selector);
if (!el) {
return;
}
if (el._flatpickr) {
el._flatpickr.destroy();
}
flatpickr(el, { dateFormat: 'm/d/Y' });
});
}
function toggleCustomDatesModal() {
const isCustom = $('#metric-rules-interval').val() === 'custom';
$('#metric-rules-custom-dates').toggle(isCustom);
}
function getRulesIntervalFromRow($row) {
return {
interval: $row.find('.metric-rules-interval-input').val() || 'overall',
custom_start: $row.find('.metric-rules-custom-start-input').val() || '',
custom_end: $row.find('.metric-rules-custom-end-input').val() || '',
};
}
function setRulesIntervalOnRow($row, intervalData) {
$row.find('.metric-rules-interval-input').val(intervalData.interval || 'overall');
$row.find('.metric-rules-custom-start-input').val(intervalData.custom_start || '');
$row.find('.metric-rules-custom-end-input').val(intervalData.custom_end || '');
}
function loadIntervalIntoModal(intervalData) {
$('#metric-rules-interval').val(intervalData.interval || 'overall');
$('#metric-rules-custom-start').val(intervalData.custom_start || '');
$('#metric-rules-custom-end').val(intervalData.custom_end || '');
toggleCustomDatesModal();
initRuleDatepickers();
}
function collectIntervalFromModal() {
const interval = $('#metric-rules-interval').val() || 'overall';
return {
interval: interval,
custom_start: interval === 'custom' ? ($('#metric-rules-custom-start').val() || '') : '',
custom_end: interval === 'custom' ? ($('#metric-rules-custom-end').val() || '') : '',
};
}
function parseRulesJson($row) {
const $input = $row.find('.metric-rules-input');
let raw = $input.val() || '[]';
if (typeof raw === 'string') {
raw = raw.trim();
}
try {
let parsed = JSON.parse(raw);
if (typeof parsed === 'string') {
parsed = JSON.parse(parsed);
}
return Array.isArray(parsed) ? parsed : [];
} catch (e) {
return [];
}
}
function getMetricNameFromRow($row) {
const name = $row.find('select.metric-select option:selected').text().trim();
if (name && name !== '-- Select Metric --') {
return name;
}
const title = $row.find('input[name*="combined_metrics[title]"]').val();
if (title && String(title).trim() !== '') {
return String(title).trim();
}
const typeName = $row.find('select[name*="combined_metrics[type]"] option:selected').text().trim();
return typeName && typeName !== '-- Select Type --' ? typeName : 'Metric';
}
function renderRangeRow(range) {
range = range || { start: 0, end: 0, weight: 0 };
return `<tr class="range-row">
<td><input type="number" class="form-control form-control-sm range-start" step="any" value="${range.start ?? 0}"></td>
<td><input type="number" class="form-control form-control-sm range-end" step="any" value="${range.end ?? 0}"></td>
<td><input type="number" class="form-control form-control-sm range-weight" step="any" value="${range.weight ?? 0}"></td>
<td><button type="button" class="btn btn-sm btn-danger btn-remove-range"><i class="mdi mdi-close"></i></button></td>
</tr>`;
}
function toggleRuleSection(ruleType) {
const $section = $(`.rule-section[data-rule-type="${ruleType}"]`);
const enabled = $section.find('.rule-enabled').is(':checked');
$section.find('.rule-section-body').toggle(enabled);
}
function loadRulesIntoModal(rules) {
RULE_TYPES.forEach(function(ruleType) {
const rule = rules.find(function(r) { return r.rule_type === ruleType; });
let ranges = rule && rule.ranges ? rule.ranges : [];
if (!Array.isArray(ranges)) {
ranges = Object.values(ranges);
}
const $section = $(`.rule-section[data-rule-type="${ruleType}"]`);
$section.find('.rule-enabled').prop('checked', ranges.length > 0);
const $tbody = $section.find('.rule-ranges tbody');
$tbody.empty();
if (ranges.length > 0) {
ranges.forEach(function(range) {
$tbody.append(renderRangeRow(range));
});
} else {
$tbody.append(renderRangeRow());
}
toggleRuleSection(ruleType);
});
}
function collectRulesFromModal() {
const rules = [];
RULE_TYPES.forEach(function(ruleType) {
const $section = $(`.rule-section[data-rule-type="${ruleType}"]`);
if (!$section.find('.rule-enabled').is(':checked')) {
return;
}
const ranges = [];
$section.find('.rule-ranges tbody tr.range-row').each(function() {
ranges.push({
start: parseFloat($(this).find('.range-start').val()) || 0,
end: parseFloat($(this).find('.range-end').val()) || 0,
weight: parseFloat($(this).find('.range-weight').val()) || 0,
});
});
if (ranges.length > 0) {
rules.push({
rule_type: ruleType,
ranges: ranges,
});
}
});
return rules;
}
function updateRulesButtonState($row) {
const rules = parseRulesJson($row);
const $btn = $row.find('.btn-metric-rules');
if (rules.length > 0) {
$btn.removeClass('btn-outline-info').addClass('btn-success').text('Rules (' + rules.length + ')');
} else {
$btn.removeClass('btn-success').addClass('btn-outline-info').text('Rules');
}
}
function initRulesButtons() {
$('#table-metrics tbody tr, #table-combined tbody tr').each(function() {
updateRulesButtonState($(this));
});
}
initRulesButtons();
$(document).on('click', '.btn-metric-rules', function() {
$activeRulesRow = $(this).closest('tr');
$('#metric-rules-modal-metric-name').text(getMetricNameFromRow($activeRulesRow));
loadRulesIntoModal(parseRulesJson($activeRulesRow));
loadIntervalIntoModal(getRulesIntervalFromRow($activeRulesRow));
if (!metricRulesModal) {
metricRulesModal = new bootstrap.Modal(document.getElementById('metric-rules-modal'));
}
metricRulesModal.show();
});
$(document).on('change', '.rule-enabled', function() {
toggleRuleSection($(this).closest('.rule-section').data('rule-type'));
});
$(document).on('change', '#metric-rules-interval', function() {
toggleCustomDatesModal();
});
$(document).on('click', '.btn-add-range', function() {
const ruleType = $(this).data('rule-type');
const $tbody = $(`.rule-section[data-rule-type="${ruleType}"] .rule-ranges tbody`);
$tbody.append(renderRangeRow());
});
$(document).on('click', '.btn-remove-range', function() {
const $tbody = $(this).closest('tbody');
if ($tbody.find('tr.range-row').length <= 1) {
$(this).closest('tr.range-row').find('input').val(0);
return;
}
$(this).closest('tr.range-row').remove();
});
$('#btn-save-metric-rules').on('click', function() {
if (!$activeRulesRow) {
return;
}
const rules = collectRulesFromModal();
$activeRulesRow.find('.metric-rules-input').val(JSON.stringify(rules));
setRulesIntervalOnRow($activeRulesRow, collectIntervalFromModal());
updateRulesButtonState($activeRulesRow);
if (metricRulesModal) {
metricRulesModal.hide();
}
});
$('#btn-add-mapping').on('click', function(){
$('table#table-metrics tbody').append(`<tr>
<td>
<select class="form-control metric-select" name="metric[]">
<option value=""> -- Select Metric -- </option>
@foreach($fields['metrics']['metrics'] as $k => $v)
<option value="{{$k}}">{{$v}}</option>
@endforeach
</select>
</td>
<td><input type="text" name="display_unit[]" class="form-control" value=""></td>
<td><input type="number" name="metric_sort[]" class="form-control" value="0"></td>
<td><input type="number" name="weight[]" class="form-control" step="any" value="1"></td>
<td><input type="text" name="label_text[]" class="form-control" value="units"></td>
<td>
<select class="form-control" name="unit_type[]">
<option value="sum" selected>Sum</option>
<option value="average">Average</option>
</select>
</td>
<td>
<input type="hidden" name="metric_rules[]" class="metric-rules-input" value="[]">
<input type="hidden" name="metric_rules_interval[]" class="metric-rules-interval-input" value="overall">
<input type="hidden" name="metric_rules_custom_start[]" class="metric-rules-custom-start-input" value="">
<input type="hidden" name="metric_rules_custom_end[]" class="metric-rules-custom-end-input" value="">
<button type="button" class="btn btn-sm btn-outline-info btn-metric-rules">Rules</button>
</td>
<th><button type="button" class="btn btn-danger waves-effect waves-light btn-remove"><i class="mdi mdi-close"></i></button></th>
</tr>`);
initMetricSelect2($('table#table-metrics tbody tr').last());
bindRemove();
});
let combinedMetricCount = 100;
$('#btn-add-combined-metric').on('click', function(){
$('table#table-combined tbody').append(`<tr>
<td>
<select class="form-control" name="combined_metrics[type][${combinedMetricCount}]" required>
<option value=""> -- Select Type -- </option>
<option value="prescribers">Prescribers (no of)</option>
<option value="unique_new">Unique New Prescribers (launch)</option>
<option value="combination">Combination (Total)</option>
</select>
</td>
<td><input type="text" name="combined_metrics[title][${combinedMetricCount}]" class="form-control" value=""></td>
<td>
<select class="form-control select2-multiple" data-toggle="select2" data-width="100%" multiple="multiple" data-placeholder="Choose Metrics" name="combined_metrics[fields][${combinedMetricCount}][]">
<option value=""> -- Select Multiple Metrics -- </option>
@foreach($fields['metrics']['metrics'] as $k => $v)
<option value="{{$k}}">{{$v}}</option>
@endforeach
</select>
</td>
<td><input type="number" name="combined_metrics[weight][${combinedMetricCount}]" class="form-control" step="any" value="1"></td>
<td><input type="text" name="combined_metrics[label_text][${combinedMetricCount}]" class="form-control" value="units"></td>
<td>
<select class="form-control" name="combined_metrics[unit_type][${combinedMetricCount}]">
<option value="sum" selected>Sum</option>
<option value="average">Average</option>
</select>
</td>
<td>
<input type="hidden" name="combined_metrics[rules][${combinedMetricCount}]" class="metric-rules-input" value="[]">
<input type="hidden" name="combined_metrics[rules_interval][${combinedMetricCount}]" class="metric-rules-interval-input" value="overall">
<input type="hidden" name="combined_metrics[rules_custom_start][${combinedMetricCount}]" class="metric-rules-custom-start-input" value="">
<input type="hidden" name="combined_metrics[rules_custom_end][${combinedMetricCount}]" class="metric-rules-custom-end-input" value="">
<button type="button" class="btn btn-sm btn-outline-info btn-metric-rules">Rules</button>
</td>
<th><button type="button" class="btn btn-danger waves-effect waves-light btn-remove-combined"><i class="mdi mdi-close"></i></button></th>
</tr>`);
$('[data-toggle="select2"]').select2();
combinedMetricCount++;
});
$(document).on('click', '.btn-remove-combined', function(){
$(this).closest('tr').remove();
});
const rankingAttrMeta = @json($fields['ranking_attributes']['attributes_meta'] ?? []);
const rankingAttrOptions = @json($fields['ranking_attributes']['attributes'] ?? []);
const rankingOptionTypes = ['select', 'multiselect', 'checkbox', 'radio'];
function escapeHtml(str) {
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
function buildRankingAttrCodeSelect(selectedCode) {
let html = '<select class="form-control ranking-attr-code" name="ranking_attribute_code[]"><option value=""> -- Select Attribute -- </option>';
Object.keys(rankingAttrOptions).forEach(function(code) {
const selected = code === selectedCode ? ' selected' : '';
html += '<option value="' + escapeHtml(code) + '"' + selected + '>' + escapeHtml(rankingAttrOptions[code]) + '</option>';
});
html += '</select>';
return html;
}
function buildRankingAttrValueField(code, selectedValue) {
const meta = rankingAttrMeta[code];
const useOptions = meta
&& rankingOptionTypes.indexOf(meta.type) !== -1
&& meta.options
&& Object.keys(meta.options).length > 0;
if (useOptions) {
let html = '<select class="form-control ranking-attr-value" name="ranking_attribute_value[]"><option value=""> -- Select Value -- </option>';
Object.keys(meta.options).forEach(function(optVal) {
const selected = String(optVal) === String(selectedValue || '') ? ' selected' : '';
html += '<option value="' + escapeHtml(optVal) + '"' + selected + '>' + escapeHtml(meta.options[optVal]) + '</option>';
});
html += '</select>';
return html;
}
return '<input type="text" class="form-control ranking-attr-value" name="ranking_attribute_value[]" value="' + escapeHtml(selectedValue || '') + '" placeholder="Enter value">';
}
function syncRankingAttrValue($row, keepValue) {
const code = $row.find('.ranking-attr-code').val() || '';
const currentValue = keepValue
? ($row.find('.ranking-attr-value').val() || '')
: '';
$row.find('.ranking-attr-value-wrap').html(buildRankingAttrValueField(code, currentValue));
}
function appendRankingAttributeRow(code, value) {
if ($('#ranking-attributes-wrapper .ranking-attributes-header').length === 0) {
$('#ranking-attributes-wrapper').prepend(`
<div class="row mb-2 ranking-attributes-header">
<div class="col-md-5"><label class="form-label">Attribute</label></div>
<div class="col-md-5"><label class="form-label">Value</label></div>
<div class="col-md-2"></div>
</div>
`);
}
const rowHtml = `<div class="row mb-2 ranking-attribute-row">
<div class="col-md-5">${buildRankingAttrCodeSelect(code || '')}</div>
<div class="col-md-5 ranking-attr-value-wrap">${buildRankingAttrValueField(code || '', value || '')}</div>
<div class="col-md-2">
<button type="button" class="btn btn-danger waves-effect waves-light btn-remove-ranking-attribute"><i class="mdi mdi-close"></i></button>
</div>
</div>`;
$('#ranking-attributes-wrapper').append(rowHtml);
}
$('#btn-add-ranking-attribute').on('click', function() {
appendRankingAttributeRow('', '');
});
$(document).on('click', '.btn-remove-ranking-attribute', function() {
$(this).closest('.ranking-attribute-row').remove();
if ($('#ranking-attributes-wrapper .ranking-attribute-row').length === 0) {
$('#ranking-attributes-wrapper .ranking-attributes-header').remove();
}
});
$(document).on('change', '.ranking-attr-code', function() {
syncRankingAttrValue($(this).closest('.ranking-attribute-row'), false);
});
});
</script>
@endsection