<?php
define('UPDATE_URL', 'https://update.uniquecoder.com/');
define('TEMP_FOLDER', FCPATH . 'uploads/temp' . '/');
define('PurchaseitemID', '16292398');

if (!function_exists('safe_strtotime')) {
	function safe_strtotime($datetime, $baseTimestamp = null)
	{
		if ($datetime === null) {
			$datetime = '';
		}
		if ($baseTimestamp === null) {
			return strtotime((string) $datetime);
		}
		return strtotime((string) $datetime, $baseTimestamp);
	}
}

if (!function_exists('normalize_lucide_icon')) {
	function normalize_lucide_icon($iconRaw, $fallback = 'circle')
	{
		$iconRaw = trim((string) $iconRaw);
		if ($iconRaw === '') {
			return $fallback;
		}

		if (preg_match('/data-lucide=["\']([^"\']+)["\']/i', $iconRaw, $match)) {
			$iconRaw = $match[1];
		}

		$iconRaw = strip_tags($iconRaw);
		$iconRaw = html_entity_decode($iconRaw, ENT_QUOTES, 'UTF-8');

		$iconRaw = str_replace('fa-fw', '', $iconRaw);
		$tokens = preg_split('/\s+/', trim($iconRaw));
		$iconName = '';

		if (is_array($tokens) && !empty($tokens)) {
			foreach ($tokens as $token) {
				if (strpos($token, 'fa-') === 0) {
					$iconName = substr($token, 3);
					break;
				}
				if (strpos($token, 'icon-') === 0) {
					$iconName = substr($token, 5);
					break;
				}
			}
			if ($iconName === '') {
				$iconName = (string) $tokens[0];
			}
		}

		$iconName = ltrim(str_replace('_', '-', trim($iconName)), '-');
		$map = array(
			'plug' => 'plug',
			'plane' => 'plane',
			'bitbucket' => 'package',
			'money' => 'badge-dollar-sign',
			'dollar' => 'badge-dollar-sign',
			'ticket' => 'ticket',
			'cogs' => 'settings',
			'database' => 'database',
			'envelope' => 'mail',
			'envelope-o' => 'mail',
			'info-circle' => 'info',
			'desktop' => 'monitor',
			'code' => 'code-2',
			'calendar' => 'calendar-days',
			'calendar-o' => 'calendar',
			'calendar-plus-o' => 'calendar-plus',
			'clock-o' => 'clock',
			'tasks' => 'list-checks',
			'users' => 'users',
			'user-secret' => 'users',
			'rocket' => 'rocket',
			'folder-open-o' => 'folder-open',
			'folder-open' => 'folder-open',
			'building-o' => 'building-2',
			'line-chart' => 'chart-line',
			'bar-chart' => 'chart-column',
			'archive' => 'archive',
			'stack-exchange' => 'boxes',
			'cube' => 'box',
			'paste' => 'clipboard-paste',
			'list-ul' => 'list',
			'camera-retro' => 'camera',
			'random' => 'shuffle',
			'circle-o' => 'circle',
			'dot-circle' => 'circle-dot',
			'dot-circle-o' => 'circle-dot',
			'sliders' => 'sliders-horizontal',
			'align-left' => 'align-left',
			'angle-right' => 'chevron-right',
			'angle-left' => 'chevron-left',
			'angle-up' => 'chevron-up',
			'angle-down' => 'chevron-down',
			'dribbble' => 'palette',
			'codepen' => 'pen-tool',
			'openpen' => 'pen-tool',
			'file-o' => 'file-text',
			'file-video-o' => 'file-video',
			'file-video' => 'file-video',
			'file-text-o' => 'file-text',
			'file-text' => 'file-text',
			'file-excel-o' => 'file-text',
			'file-excel' => 'file-text',
			'excel' => 'file-text',
			'pencil-square' => 'file-pen-line',
			'pencil-square-o' => 'refresh-cw',
			'pagelines' => 'calendar-minus',
			'certificate' => 'badge-dollar-sign',
			'star-o' => 'list-plus',
			'contao' => 'timer-reset',
			'bell-o' => 'bell',
			'language' => 'languages',
			'dashboard' => 'layout-dashboard',
			'question-circle' => 'circle-help',
			'speedometer' => 'gauge',
			'tachometer' => 'gauge',
			'share-alt' => 'share-2',
			'cubes' => 'boxes',
			'bullseye' => 'target',
			'bullhorn' => 'megaphone',
			'suitcase' => 'briefcase-business',
			'warning' => 'shield-alert',
			'arrow-circle-up' => 'circle-arrow-up',
			'cc-mastercard' => 'credit-card',
			'usd' => 'badge-dollar-sign',
		);

		if (isset($map[$iconName])) {
			$iconName = $map[$iconName];
		}

		if ($iconName === '' || strpos((string) $iconName, 'fa-') === 0 || $iconName === 'fa') {
			return $fallback;
		}

		return $iconName;
	}
}


/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */


/*function config_item($key)
{
    $CI =& get_instance();
    return $CI->db->query("SELECT value FROM tbl_config WHERE config_key = '$key'")->row()->value;
    return $config_items[$key];
}*/
function action_btn($uri, $label, $style = 'primary', $icon = '', $size = 'xs', $extra = array(), $confirm = null)
{
	$extra_attributes = '';
	if (!is_array($extra)) {
		$extra_attributes = (string) $extra;
		$extra = array();
	}

	$classes = trim("btn mb-1 h-7 gap-0.5 btn-$style btn-$size " . (!empty($extra['class']) ? $extra['class'] . ' ml-1' : ' ml-1'));
	unset($extra['class']);
	$attrs = array_merge(array(
		'class' => $classes,
		'title' => $label,
		'data-toggle' => 'tooltip',
		'data-placement' => 'top',
	), $extra);
	if ($confirm) {
		$attrs['data-confirm-message'] = $confirm;
	}
	$iconHtml = $icon ? '<i data-lucide="' . $icon . '" class="h-3.5 w-3.5"></i> ' : '';
	if (!empty($attrs['data-act']) && $attrs['data-act'] === 'ajax-request') {
		if (empty($attrs['data-action-url'])) {
			$attrs['data-action-url'] = base_url($uri);
		}
		return '<a href="#"' . _stringify_attributes($attrs) . $extra_attributes . '>' . $iconHtml . $label . '</a>';
	}
	if ($extra_attributes !== '') {
		$href = preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri);
		return '<a href="' . html_escape($href) . '"' . _stringify_attributes($attrs) . $extra_attributes . '>' . $iconHtml . $label . '</a>';
	}
	return anchor($uri, $iconHtml . $label, $attrs);
}

function btn_delete_ajax($uri, $data_fade_id = null)
{
	$fade_out_attr = array();
	if ($data_fade_id) {
		// if data_fade_id is numeric then append table_ prefix elase use as it is
		if (is_numeric($data_fade_id)) {
			$data_fade_id = 'table_' . $data_fade_id;
		}
		$fade_out_attr = array("data-fade-out-on-success" => "#" . $data_fade_id);
	}
	return action_btn($uri, lang('delete'), 'danger', 'trash-2', 'xs', array_merge(array(
		'class' => 'js-ajax-delete',
		'title' => lang('delete'),
		'data-act' => 'ajax-request',
		'data-action-url' => base_url($uri),
		'data-confirm-action' => 'delete',
		'data-confirm-title' => lang('delete'),
		'data-confirm-accept-label' => lang('delete'),
		'data-confirm-message' => lang('delete_alert'),
	), $fade_out_attr));
}

function btn_edit($uri)
{
	return action_btn($uri, '', 'primary', 'square-pen');
}

function btn_update($label = 'update', $size = 'xs', $h = 9)
{
	return "<button data-toggle='tooltip' title=" . lang($label) . " data-placement='top' type='submit'  class='update_kr btn ml-1 btn-primary h-$h mb-1 btn-$size'><i data-lucide='check-circle' class='h-3.5 w-3.5 mr-2'></i> " . lang($label) . "</button>";
}
function btn_modal_close($label = 'close', $size = 'xs')
{
	return "<button data-toggle='tooltip' title=" . lang($label) . " data-placement='top' type='button' data-modal-close class='btn py-2 h-9 mb-1 ml-1 btn-default btn-$size'><i data-lucide='x' class='h-3.5 w-3.5 mr-1'></i> " . lang($label) . "</button>";
}
function btn_cancel($uri, $label = 'cancel', $size = 'xs', $icon = 'x', array $extra = array())
{
	return action_btn($uri, lang($label), 'danger', $icon, $size, $extra);
}
function btn_edit_disable($u)
{
	return action_btn($u, lang('edit'), 'secondary disabled', 'square-pen');
}
function btn_edit_modal($uri)
{
	return action_btn($uri, lang('edit'), 'primary', 'square-pen', 'xs', array('data-toggle' => 'modal', 'data-target' => '#myModal'));
}
function btn_banned_modal($u)
{
	return action_btn($u, lang('banned'), 'danger', 'ban', 'xs', array('data-toggle' => 'modal', 'data-target' => '#myModal', 'class' => 'btn_modal'));
}

function btn_delete($uri, $text = null, $icon = null)
{
	$label = $text ?: lang('delete');
	$ico = $icon ?: 'trash-2';
	return action_btn($uri, $label, 'danger', $ico, 'xs', array(
		'class' => 'deleteBtn',
		'data-confirm-action' => 'delete',
		'data-confirm-title' => lang('delete'),
		'data-confirm-accept-label' => lang('delete'),
	), lang('delete_alert'));
}

function btn_delete_disable($uri)
{
	return action_btn($uri, lang('delete'), 'danger disabled', 'trash-2');
}

function btn_active($uri)
{
	return action_btn($uri, lang('active'), 'success', 'check', 'xs', array(), lang('confirm_activation'));
}

function btn_print()
{
	return "<button class='btn btn-primary btn-xs' data-toggle='tooltip' title='" . lang('print') . "' onclick='printDiv(\"printableArea\")'><i data-lucide='printer'></i></button>";
}

function btn_atndc_print()
{
	return "<button class='btn btn-customs btn-xs' data-toggle='tooltip' title='" . lang('print') . "' onclick='printDiv(\"printableArea\")'><i data-lucide='printer'></i></button>";
}

function btn_pdf($uri)
{
	return action_btn($uri, 'PDF', 'info', 'file-text');
}
function btn_make_pdf($uri)
{
	return action_btn($uri, lang('generate_pdf'), 'primary', 'file-text');
}
function btn_excel($uri)
{
	return action_btn($uri, 'Excel', 'primary', 'file-type');
}
function btn_view($uri, $size = 'xs')
{
	return action_btn($uri, '', 'info', 'list-tree', $size);
}
function btn_view_modal($u)
{
	return action_btn($u, lang('view'), 'info', 'list-tree', 'xs', array('data-toggle' => 'modal', 'data-target' => '#myModal_lg'));
}
function btn_save($uri)
{
	return action_btn($uri, lang('save'), 'success', 'plus');
}
function btn_add($label = 'add', $size = 'xs', $h = 4)
{
	return "<button type='submit' name='add' value='1' class='btn h-7 btn-info btn-$size'> <i data-lucide='plus' class='h-$h w-$h mr-1'></i> " . lang($label) . "</button>";
}

function btn_copy($uri)
{
	return action_btn($uri, lang('copy'), 'success', 'copy', 'xs', array(), lang('confirm_copy'));
}

function btn_publish($uri)
{
	return action_btn($uri, lang('click_to_published'), 'success', 'check', 'xs', array(
		'data-confirm-action' => 'publish',
		'data-confirm-title' => lang('publish'),
		'data-confirm-accept-label' => lang('publish'),
	), lang('confirm_publish'));
}
function btn_unpublish($uri)
{
	return action_btn($uri, lang('click_to_unpublished'), 'danger', 'x', 'xs', array(
		'data-confirm-action' => 'unpublish',
		'data-confirm-title' => lang('unpublish'),
		'data-confirm-accept-label' => lang('unpublish'),
	), lang('confirm_unpublish'));
}
function btn_approve($uri)
{
	return action_btn($uri, lang('click_to_reject'), 'danger', 'x', 'xs', array(), lang('confirm_unpublish'));
}
function btn_reject($uri)
{
	return action_btn($uri, lang('click_to_approve'), 'success', 'check', 'xs', array(), lang('confirm_publish'));
}

// Extra generic buttons
function btn_primary($uri, $label, $icon = '', $size = 'xs')
{
	return action_btn($uri, $label, 'primary', $icon, $size);
}
function btn_secondary($uri, $label, $icon = '', $size = 'xs')
{
	return action_btn($uri, $label, 'secondary', $icon, $size);
}
function btn_success($uri, $label, $icon = '', $size = 'xs')
{
	return action_btn($uri, $label, 'success', $icon, $size);
}
function btn_danger($uri, $label, $icon = '', $size = 'xs')
{
	return action_btn($uri, $label, 'danger', $icon, $size);
}
function btn_info($uri, $label, $icon = '', $size = 'xs')
{
	return action_btn($uri, $label, 'info', $icon, $size);
}
function btn_mail($uri)
{
	return action_btn($uri, ' ', 'success', 'mail', 'xs');
}


// btn_back
function btn_back($uri, $label = 'back', $size = 'xs')
{
	return action_btn($uri, lang($label), 'popover', 'arrow-left', $size);
}
/**
 * Get current url with query vars
 *
 * @return string
 */
function current_full_url()
{
	$CI  = &get_instance();
	$url = $CI->config->site_url($CI->uri->uri_string());
	$queryString = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';

	return $queryString !== '' ? $url . '?' . $queryString : $url;
}

/**
 * Build a string of HTML attributes from array.
 */
function build_html_attributes(array $attrs = array())
{
	$out = array();
	foreach ($attrs as $key => $val) {
		if ($val === null || $val === false) {
			continue;
		}
		if ($val === true) {
			$out[] = $key;
			continue;
		}
		$out[] = $key . '="' . html_escape($val) . '"';
	}

	return implode(' ', $out);
}

/**
 * Tailwind friendly input renderer (stacked).
 */
function form_control($name, $label, $value = '', $type = 'text', $required = false, array $attrs = array())
{
	return form_input_group($name, $label, $value, $type, $required, $attrs);
}

/**
 * Render stacked or horizontal input with optional prepend/append addons.
 */
function form_input_group($name, $label, $value = '', $type = 'text', $required = false, array $attrs = array(), $prepend = '', $append = '', $layout = 'stacked',)
{
	$id = isset($attrs['id']) ? $attrs['id'] : $name;
	$classes = 'w-full rounded-lg border border-border bg-input px-3 py-2 text-sm dark:focus:border-secondary  dark:focus:ring-3  dark:focus:ring-secondary/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-secondary/30';
	if (!empty($attrs['class'])) {
		$classes .= ' ' . $attrs['class'];
	}
	// if prepend then remove left rounded
	if ($prepend) {
		$classes .= ' rounded-l-none ';
	}
	// if append then remove right rounded
	if ($append) {
		$classes .= ' rounded-r-none ';
	}

	$attrs['id'] = $id;
	$attrs['name'] = $name;
	$attrs['class'] = $classes;
	$attrs['type'] = $type;
	if ($required) {
		$attrs['required'] = 'required';
	}
	$attrString = build_html_attributes($attrs);
	$badge = $required ? ' <span class="text-red-500">*</span>' : '';

	$inputEl = '<input ' . $attrString . ' value="' . html_escape($value) . '">';
	if ($prepend || $append) {
		$inputEl = '<div class="flex items-center rounded-lg  bg-input overflow-hidden">'
			. ($prepend ? '<span class="px-4 py-2 text-sm text-muted-foreground rounded-l-lg border border-r-0 border-border bg-input">' . $prepend . '</span>' : '')
			. '<div class="flex-1">' . $inputEl . '</div>'
			. ($append ? '<span class="px-4 py-2 text-sm text-muted-foreground rounded-r-lg border border-l-0 border-border bg-input">' . $append . '</span>' : '')
			. '</div>';
	}

	if ($layout === 'horizontal') {
		return '<div class="flex items-start gap-3 py-2">'
			. '<label for="' . html_escape($id) . '" class="w-48 text-sm font-medium text-foreground pt-2 text-right">' . $label . $badge . '</label>'
			. '<div class="flex-1">' . $inputEl . '</div>'
			. '</div>';
	}

	return '<label class="space-y-1 text-sm font-medium text-foreground block">'
		. ($label ? '<span>' . $label . $badge . '</span>' : '')
		. $inputEl
		. '</label>';
}

/**
 * Tailwind friendly textarea renderer (stacked).
 */
function form_textarea($name, $label = '', $value = '', $rows = 3, $required = false, array $attrs = array(), $layout = 'stacked')
{
	if (is_array($name)) {
		$attrs = $name;
		$name = isset($attrs['name']) ? $attrs['name'] : '';
		$label = isset($attrs['label']) ? $attrs['label'] : '';
		$value = isset($attrs['value']) ? $attrs['value'] : '';
		if (isset($attrs['rows'])) {
			$rows = (int)$attrs['rows'];
		}
		if (isset($attrs['required'])) {
			$required = (bool)$attrs['required'];
		}
		unset($attrs['name'], $attrs['label'], $attrs['value'], $attrs['rows'], $attrs['required']);
	}
	return form_textarea_group($name, $label, $value, $rows, $required, $attrs, $layout);
}

/**
 * Textarea with optional editor class, stacked or horizontal, and addons.
 */
function form_textarea_group($name, $label, $value = '', $rows = 3, $required = false, array $attrs = array(), $layout = 'stacked', $prepend = '', $append = '', $editor = false)
{
	$id = isset($attrs['id']) ? $attrs['id'] : $name;
	$classes = 'w-full rounded-lg border border-border bg-input px-3 py-2 text-sm  dark:focus:border-secondary dark:focus:ring-3 dark:focus:ring-secondary/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-secondary/30';
	if ($editor) {
		$classes .= ' js-editor hidden';
	}
	if (!empty($attrs['class'])) {
		$classes .= ' ' . $attrs['class'];
	}

	// check if class have tw-editor then add h-full
	// if (strpos($classes, 'tw-editor') !== false) {
	// 	$classes .= ' h-full ';
	// }


	$attrs['id'] = $id;
	$attrs['name'] = $name;
	$attrs['class'] = $classes;
	$attrs['rows'] = $rows;
	if ($required) {
		$attrs['required'] = 'required';
	}
	$attrString = build_html_attributes($attrs);
	$badge = $required ? ' <span class="text-red-500">*</span>' : '';

	$inputEl = '<textarea ' . $attrString . '>' . html_escape($value) . '</textarea>';
	if ($editor) {
		return '<div class="bg-white dark:bg-slate-800 rounded-xl shadow-sm border border-slate-200 dark:border-slate-700 overflow-hidden transition-colors">'
			. ($label ? '<div class="p-3 bg-slate-50 dark:bg-slate-700/50 border-b border-slate-200 dark:border-slate-700 font-semibold text-sm text-slate-600 dark:text-slate-300">' . $label . $badge . '</div>' : '')
			. $inputEl
			. '</div>';
	}
	if ($prepend || $append) {
		$inputEl = '<div class="flex items-stretch rounded-lg border border-border bg-background overflow-hidden">'
			. ($prepend ? '<span class="px-3 py-2 text-sm text-muted-foreground border-r border-border bg-muted">' . $prepend . '</span>' : '')
			. '<div class="flex-1">' . $inputEl . '</div>'
			. ($append ? '<span class="px-3 py-2 text-sm text-muted-foreground border-l border-border bg-muted">' . $append . '</span>' : '')
			. '</div>';
	}

	if ($layout === 'horizontal') {
		return '<div class="flex items-start gap-3 py-2">'
			. ($label ? '<label for="' . html_escape($id) . '" class="w-48 text-sm font-medium text-foreground pt-2 text-right">' . $label . $badge . '</label>' : '')
			. '<div class="flex-1">' . $inputEl . '</div>'
			. '</div>';
	}

	return '<label class="space-y-1 text-sm font-medium text-foreground block">'
		. '<span>' . $label . $badge . '</span>'
		. $inputEl
		. '</label>';
}

/**
 * Modern checkbox control (single).
 */
function form_checkbox($name, $label, $checked = false, $required = false, array $attrs = array(), $layout = 'stacked')
{
	$id = isset($attrs['id']) ? $attrs['id'] : $name . '_' . substr(md5($name . $label), 0, 6);

	$inputClasses = 'peer appearance-none w-5 h-5 border border-slate-300 dark:border-slate-600 rounded bg-white dark:bg-slate-900 checked:bg-blue-600 checked:border-blue-600 transition-all';
	if (!empty($attrs['class'])) {
		$inputClasses .= ' ' . $attrs['class'];
	}
	$attrs['id'] = $id;
	$attrs['name'] = $name;
	$attrs['type'] = 'checkbox';
	$attrs['class'] = $inputClasses;
	if ($checked) {
		$attrs['checked'] = 'checked';
	}
	if ($required) {
		$attrs['required'] = 'required';
	}
	$attrString = build_html_attributes($attrs);
	$badge = $required ? ' <span class="text-red-500">*</span>' : '';

	$item = '<label for="' . html_escape($id) . '" class="flex items-center gap-3 cursor-pointer">
		<div class="relative flex items-center justify-center w-5 h-5">
			<input ' . $attrString . '>
			<div class="absolute inset-0 flex items-center justify-center text-white scale-0 peer-checked:scale-100 transition-transform pointer-events-none">
				<i data-lucide="check" class="w-5 h-5 dark:text-blue-500"></i>
			</div>
		</div>
		<span class="text-sm text-foreground">' . $label . $badge . '</span>
	</label>';

	if ($layout === 'horizontal') {
		return '<div class="flex items-center gap-3 py-2">' . $item . '</div>';
	}

	return '<div class="py-1">' . $item . '</div>';
}

/**
 * Modern radio control (single).
 */
function form_radio($name, $label, $value, $checked = false, $required = false, array $attrs = array(), $layout = 'stacked')
{
	$id = isset($attrs['id']) ? $attrs['id'] : $name . '_' . substr(md5($name . $value), 0, 6);
	$inputClasses = 'peer appearance-none w-5 h-5 border-2 border-slate-300 dark:border-slate-600 rounded-full bg-white dark:bg-slate-800 checked:border-blue-600 transition-all';
	if (!empty($attrs['class'])) {
		$inputClasses .= ' ' . $attrs['class'];
	}
	$attrs['id'] = $id;
	$attrs['name'] = $name;
	$attrs['type'] = 'radio';
	$attrs['value'] = $value;
	$attrs['class'] = $inputClasses;
	if ($checked) {
		$attrs['checked'] = 'checked';
	}
	if ($required) {
		$attrs['required'] = 'required';
	}
	$attrString = build_html_attributes($attrs);
	$badge = $required ? ' <span class="text-red-500">*</span>' : '';

	$item = '<label for="' . html_escape($id) . '" class="flex items-center gap-3 cursor-pointer">
		<div class="relative flex items-center justify-center w-5 h-5">
			<input ' . $attrString . '>
			<div class="absolute w-2.5 h-2.5 top-[5px] rounded-full bg-blue-600 scale-0 peer-checked:scale-100 transition-transform"></div>
		</div>
		<span class="text-sm text-slate-700 dark:text-slate-300 group-hover:text-slate-900 dark:group-hover:text-white transition-colors">' . $label . $badge . '</span>
	</label>';

	if ($layout === 'horizontal') {
		return '<div class="flex items-center gap-3 py-2">' . $item . '</div>';
	}

	return '<div class="py-1">' . $item . '</div>';
}

/**
 * Checkbox group renderer.
 */
function form_checkbox_group($name, $label, array $options = array(), array $selected = array(), $required = false, array $attrs = array(), $layout = 'stacked')
{
	$badge = $required ? ' <span class="text-red-500">*</span>' : '';
	$wrapClass = 'grid grid-cols-1 sm:grid-cols-2 gap-3';
	if (!empty($attrs['group_class'])) {
		$wrapClass .= ' ' . $attrs['group_class'];
		unset($attrs['group_class']);
	}
	if (substr($name, -2) !== '[]') {
		$name .= '[]';
	}
	$items = '';
	foreach ($options as $value => $optLabel) {
		$isChecked = in_array((string)$value, array_map('strval', $selected), true);
		$itemAttrs = $attrs;
		$itemAttrs['value'] = $value;
		$items .= form_checkbox($name, $optLabel, $isChecked, $required && empty($items), $itemAttrs, 'stacked');
	}

	if ($layout === 'horizontal') {
		return '<div class="flex items-start gap-3 py-2">'
			. '<label class="w-48 text-sm font-medium text-foreground pt-1">' . $label . $badge . '</label>'
			. '<div class="' . $wrapClass . '">' . $items . '</div>'
			. '</div>';
	}

	return '<div class="space-y-2">'
		. '<div class="text-sm font-medium text-foreground">' . $label . $badge . '</div>'
		. '<div class="' . $wrapClass . '">' . $items . '</div>'
		. '</div>';
}

/**
 * Radio group renderer.
 */
function form_radio_group($name, $label, array $options = array(), $selected = '', $required = false, array $attrs = array(), $layout = 'stacked')
{
	$badge = $required ? ' <span class="text-red-500">*</span>' : '';
	$wrapClass = 'grid grid-cols-1 sm:grid-cols-2 gap-3';
	if (!empty($attrs['group_class'])) {
		$wrapClass .= ' ' . $attrs['group_class'];
		unset($attrs['group_class']);
	}
	$items = '';
	foreach ($options as $value => $optLabel) {
		$isChecked = ((string)$value === (string)$selected);
		$itemAttrs = $attrs;
		$items .= form_radio($name, $optLabel, $value, $isChecked, $required && empty($items), $itemAttrs, 'stacked');
	}

	if ($layout === 'horizontal') {
		return '<div class="flex items-start gap-3 py-2">'
			. '<label class="w-48 text-sm font-medium text-foreground pt-1 text-end">' . $label . $badge . '</label>'
			. '<div class="' . $wrapClass . '">' . $items . '</div>'
			. '</div>';
	}

	return '<div class="space-y-2">'
		. '<div class="text-sm font-medium text-foreground">' . $label . $badge . '</div>'
		. '<div class="' . $wrapClass . '">' . $items . '</div>'
		. '</div>';
}

/**
 * Segmented yes/no (or custom) switch-style radio group.
 */
function form_switch_radio($name, $label, $selected = 'Yes', $required = false, array $attrs = array(), $layout = 'stacked', array $options = array('Yes' => 'Yes', 'No' => 'No'))
{
	$badge = $required ? ' <span class="text-red-500">*</span>' : '';
	$selected = (string)$selected;
	$wrapClass = 'inline-flex rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 overflow-hidden';
	if (!empty($attrs['group_class'])) {
		$wrapClass .= ' ' . $attrs['group_class'];
		unset($attrs['group_class']);
	}

	$items = '';
	$error_html = form_error($name, '<p class="text-xs font-medium text-rose-600">', '</p>');
	$index = 0;
	foreach ($options as $value => $text) {
		$value = (string)$value;
		$id = $name . '_' . substr(md5($name . $value), 0, 8);
		$isChecked = ($value === $selected);
		$inputAttrs = $attrs;
		$inputAttrs['id'] = $id;
		$inputAttrs['name'] = $name;
		$inputAttrs['type'] = 'radio';
		$inputAttrs['value'] = $value;
		$inputAttrs['class'] = 'sr-only peer';
		if ($isChecked) {
			$inputAttrs['checked'] = 'checked';
		}
		if ($required && $index === 0) {
			$inputAttrs['required'] = 'required';
		}
		$attrString = build_html_attributes($inputAttrs);
		$activeClass = ($value === 'Yes') ? 'peer-checked:bg-emerald-500' : 'peer-checked:bg-slate-700';
		$items .= '<label for="' . html_escape($id) . '" class="relative cursor-pointer">'
			. '<input ' . $attrString . '>'
			. '<span class="block px-4 py-2 text-xs font-semibold text-slate-600 dark:text-slate-300 bg-white dark:bg-slate-900 transition-colors ' . $activeClass . ' peer-checked:text-white">'
			. html_escape($text) . '</span>'
			. '</label>';
		$index++;
	}

	$control = '<div class="' . $wrapClass . '">' . $items . '</div>';

	if ($layout === 'horizontal') {
		return '<div class="flex items-center gap-4 py-2">'
			. '<label class="w-48 text-sm font-medium text-foreground text-right">' . $label . $badge . '</label>'
			. '<div class="space-y-2">' . $control . $error_html . '</div>'
			. '</div>';
	}

	return '<div class="space-y-2">'
		. '<div class="text-sm font-medium text-foreground">' . $label . $badge . '</div>'
		. $control
		. $error_html
		. '</div>';
}

/**
 * Tailwind friendly file input renderer.
 */
function form_file($name, $label, $required = false, array $attrs = array())
{
	return form_input_group($name, $label, '', 'file', $required, $attrs, '');
}

/**
 * Select dropdown renderer with stacked/horizontal layout and addons.
 */
function form_select_group($name, $label, array $options = array(), $selected = '', $required = false, array $attrs = array(), $prepend = '', $append = '', $layout = 'stacked')
{
	$id = isset($attrs['id']) ? $attrs['id'] : $name;
	$classes = 'w-full rounded-lg border border-border bg-input px-3 py-2 text-sm  dark:focus:border-secondary dark:focus:ring-3 dark:focus:ring-secondary/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-secondary/30 flex items-center justify-between tw-selectpicker';
	if (!empty($attrs['class'])) {
		$classes .= ' ' . $attrs['class'];
	}
	if ($prepend) {
		$classes .= ' rounded-l-none ';
	}
	if ($append) {
		$classes .= ' rounded-r-none ';
	}
	$attrs['id'] = $id;
	$attrs['name'] = $name;
	$attrs['class'] = $classes;
	if (empty($attrs['data-placeholder'])) {
		$attrs['data-placeholder'] = lang('nothing_selected') ?: 'Nothing selected';
	}
	if ($required) {
		$attrs['required'] = 'required';
	}
	$attrString = build_html_attributes($attrs);
	$badge = $required ? ' <span class="text-red-500">*</span>' : '';

	$selected_values = is_array($selected)
		? array_map('strval', $selected)
		: array((string) $selected);

	$optsHtml = '';
	foreach ($options as $key => $value) {
		$is_group = is_array($value) || (is_object($value) && isset($value->items));
		if ($is_group) {
			$group_label = '';
			$items = array();
			if (is_array($value)) {
				if (isset($value['label']) && isset($value['items']) && is_array($value['items'])) {
					$group_label = $value['label'];
					$items = $value['items'];
				} else {
					$group_label = is_string($key) ? $key : '';
					$items = $value;
				}
			} elseif (is_object($value)) {
				$group_label = isset($value->label) ? $value->label : (is_string($key) ? $key : '');
				$items = isset($value->items) && is_array($value->items) ? $value->items : array();
			}
			if ($group_label !== '' && !empty($items)) {
				$optsHtml .= '<optgroup label="' . html_escape($group_label) . '">';
				foreach ($items as $val => $text) {
					$sel = in_array((string) $val, $selected_values, true) ? ' selected' : '';
					$optsHtml .= '<option value="' . html_escape($val) . '"' . $sel . '>' . html_escape($text) . '</option>';
				}
				$optsHtml .= '</optgroup>';
			}
			continue;
		}

		$sel = in_array((string) $key, $selected_values, true) ? ' selected' : '';
		$optsHtml .= '<option value="' . html_escape($key) . '"' . $sel . '>' . html_escape($value) . '</option>';
	}

	$inputEl = '<select ' . $attrString . '>' . $optsHtml . '</select>';
	if ($prepend || $append) {
		$inputEl = '<div class="flex items-start">'
			. ($prepend ? '<span class="inline-flex group cursor-pointer h-[38px] w-10 items-center justify-center text-sm bg-background text-muted-foreground transition-colors hover:bg-muted hover:text-foreground rounded-l-lg border border-r-0 border-border bg-input">' . $prepend . '</span>' : '')
			. '<div class="flex-1">' . $inputEl . '</div>'
			. ($append ? '<span class="inline-flex group cursor-pointer h-[38px] w-10 items-center justify-center text-sm bg-background text-muted-foreground transition-colors hover:bg-muted hover:text-foreground rounded-r-lg border border-l-0 border-border bg-input">' . $append . '</span>' : '')
			. '</div>';
	}

	if ($layout === 'horizontal') {
		return '<div class="flex items-start gap-3 py-2">'
			. '<label for="' . html_escape($id) . '" class="w-48 text-sm font-medium text-foreground pt-2 text-right">' . $label . $badge . '</label>'
			. '<div class="flex-1">' . $inputEl . '</div>'
			. '</div>';
	}

	return '<label class="space-y-1 text-sm font-medium text-foreground block">'
		. '<span>' . $label . $badge . '</span>'
		. $inputEl
		. '</label>';
}

/**
 * Generic lookup input (dynamic search + hidden id).
 */
function form_lookup_group($name, $label, $selected_id = '', $selected_label = '', $required = false, array $attrs = array(), $wrapper_class = 'md:col-span-2', array $config = array(), $layout = 'stacked')
{
	$type = '';
	if (!empty($config['type'])) {
		$type = $config['type'];
	} else {
		$type = preg_replace('/_id$/', '', $name);
	}
	$type = trim((string) $type);
	if ($type === '') {
		$type = 'project';
	}

	$input_id = isset($attrs['id']) ? $attrs['id'] : $name . '_lookup';
	// $input_classes = 'w-full rounded-lg border border-border bg-input px-3 py-2 text-sm dark:focus:border-secondary dark:focus:ring-3 dark:focus:ring-secondary/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-secondary/30';
	$input_classes = 'w-full rounded-lg border border-border bg-input px-3 py-2 pr-10 text-sm  dark:focus:border-secondary dark:focus:ring-3 dark:focus:ring-secondary/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-secondary/30 ';

	if (!empty($attrs['class'])) {
		$input_classes .= ' ' . $attrs['class'];
	}
	$append = !empty($config['append']) ? $config['append'] : '';
	$prepend = !empty($config['prepend']) ? $config['prepend'] : '';
	if ($prepend) {
		$input_classes .= ' rounded-l-none ';
	}
	if ($append) {
		$input_classes .= ' rounded-r-none ';
	}

	$placeholder = !empty($config['placeholder'])
		? $config['placeholder']
		: (lang('search') . ' ' . $label);

	$input_attrs = array_merge($attrs, array(
		'id' => $input_id,
		'type' => 'text',
		'class' => $input_classes,
		'autocomplete' => 'off',
		'data-lookup-input' => '1',
	));
	if (empty($input_attrs['placeholder'])) {
		$input_attrs['placeholder'] = $placeholder;
	}
	$input_attr_string = build_html_attributes($input_attrs);
	$badge = $required ? ' <span class="text-red-500">*</span>' : '';

	$wrapper_classes = trim($wrapper_class . ($layout === 'horizontal' ? '' : ' space-y-1'));
	$wrapper_attrs = array(
		'class' => $wrapper_classes,
		'data-lookup' => '1',
		'data-lookup-type' => $type,
		'data-lookup-limit' => array_key_exists('limit', $config) ? (int) $config['limit'] : 4,
		'data-lookup-min-length' => array_key_exists('min_length', $config) ? (int) $config['min_length'] : 0,
	);
	if (!empty($config['endpoint'])) {
		$wrapper_attrs['data-lookup-endpoint'] = $config['endpoint'];
	}
	if (!empty($config['onchange'])) {
		$wrapper_attrs['data-lookup-onchange'] = $config['onchange'];
	}
	if (!empty($config['depends'])) {
		$wrapper_attrs['data-lookup-depends'] = $config['depends'];
	}
	if (!empty($config['extra'])) {
		$wrapper_attrs['data-lookup-extra'] = json_encode($config['extra']);
	}

	$wrapper_attr_string = build_html_attributes($wrapper_attrs);

	$results = '<div class="absolute z-30 mt-1 w-full rounded-lg border border-border bg-card shadow-lg hidden max-h-56 overflow-y-auto" data-lookup-results></div>';
	$clear_button = '<button type="button" class="absolute right-2 top-1/2 hidden -translate-y-1/2 rounded-full border border-border bg-background p-1 text-muted-foreground shadow-sm hover:bg-muted hover:text-foreground" data-lookup-clear ' . (!empty($selected_id) ? '' : 'tabindex="-1"') . '>'
		. '<i data-lucide="x" class="h-3.5 w-3.5"></i>'
		. '</button>';


	$input_html = '<input ' . $input_attr_string . ' value="' . html_escape($selected_label) . '">';
	if (!empty($append)) {
		$input_attrs['class'] .= ' flex-1';
		$input_attr_string = build_html_attributes($input_attrs);
		$input_html = '<div class="flex items-center">'
			. ($prepend ? '<span class="inline-flex group cursor-pointer h-[38px] w-10 items-center justify-center text-sm bg-background text-muted-foreground transition-colors hover:bg-muted hover:text-foreground rounded-l-lg border border-r-0 border-border bg-input">' . $prepend . '</span>' : '')
			. '<input ' . $input_attr_string . ' value="' . html_escape($selected_label) . '">'
			. ($append ? '<span class="inline-flex group cursor-pointer h-[38px] w-10 items-center justify-center text-sm bg-background text-muted-foreground transition-colors hover:bg-muted hover:text-foreground rounded-r-lg border border-l-0 border-border bg-input">' . $append . '</span>' : '')
			. '</div>';
	}

	if ($layout === 'horizontal') {
		return '<div ' . $wrapper_attr_string . '>'
			. '<div class="flex items-start gap-3 py-2">'
			. '<label for="' . html_escape($input_id) . '" class="w-48 text-sm font-medium text-foreground pt-2">' . $label . $badge . '</label>'
			. '<div class="flex-1">'
			. '<input type="hidden" name="' . html_escape($name) . '" value="' . html_escape($selected_id) . '" data-lookup-hidden>'
			. '<div class="relative">'
			. $input_html
			. $clear_button
			. $results
			. '</div>'
			. '<p class="text-xs text-rose-500 hidden" data-lookup-status>' . $placeholder . '</p>'
			. '</div>'
			. '</div>'
			. '</div>';
	}

	return '<div ' . $wrapper_attr_string . '>'
		. '<label class="space-y-1 text-sm font-medium text-foreground block">'
		. '<span>' . $label . $badge . '</span>'
		. '<input type="hidden" name="' . html_escape($name) . '" value="' . html_escape($selected_id) . '" data-lookup-hidden>'
		. '<div class="relative">'
		. $input_html
		. $clear_button
		. $results
		. '</div>'
		. '</label>'
		. '<p class="text-xs text-rose-500 hidden" data-lookup-status>' . $placeholder . '</p>'
		. '</div>';
}

function form_grouped_lookup_group($name, $label, array $groups = array(), $selected_id = '', $selected_label = '', $required = false, array $attrs = array(), $wrapper_class = 'md:col-span-2', array $config = array(), $layout = 'stacked')
{
	$input_id = isset($attrs['id']) ? $attrs['id'] : $name . '_grouped_lookup';
	$input_classes = 'w-full min-h-10 rounded-xl border border-slate-300 bg-white py-2 pl-10 pr-10 text-sm text-slate-900 shadow-sm transition placeholder:text-slate-400 focus:border-secondary focus:outline-none focus:ring-4 focus:ring-secondary/15 dark:border-slate-700 dark:bg-slate-950 dark:text-slate-100 dark:placeholder:text-slate-500 dark:focus:border-secondary dark:focus:ring-secondary/25';
	if (!empty($attrs['class'])) {
		$input_classes .= ' ' . $attrs['class'];
	}
	$placeholder = !empty($config['placeholder'])
		? $config['placeholder']
		: (lang('search') . ' ' . $label);
	$helper_text = isset($config['helper']) ? $config['helper'] : '';
	$append = isset($config['append']) ? $config['append'] : '';
	$badge = $required ? ' <span class="text-red-500">*</span>' : '';
	$append_html = $append;
	if (!empty($append_html)) {
		$input_classes .= ' rounded-r-none border-r-0';
	}
	if (is_string($append_html) && $append_html !== '') {
		$append_html = str_replace(array(' mb-1', ' ml-1'), '', $append_html);
			$append_btn_classes = 'inline-flex h-[2.4rem] w-10 items-center justify-center rounded-l-none rounded-r-lg border border-border border-l bg-background text-muted-foreground transition-colors hover:bg-muted hover:text-foreground';
		if (preg_match('/<(a|button)\b/i', $append_html)) {
			if (preg_match('/class=(["\'])(.*?)\1/i', $append_html)) {
				$append_html = preg_replace('/class=(["\'])(.*?)\1/i', 'class=$1$2 ' . $append_btn_classes . '$1', $append_html, 1);
			} else {
				$append_html = preg_replace('/<(a|button)\b/i', '<$1 class="' . $append_btn_classes . '"', $append_html, 1);
			}
		}
	}

	$normalized_groups = array();
	foreach ($groups as $group) {
		$group_label = '';
		$items = array();
		if (is_array($group)) {
			$group_label = isset($group['label']) ? $group['label'] : (isset($group['department']) ? $group['department'] : '');
			$items = isset($group['items']) && is_array($group['items']) ? $group['items'] : array();
		} elseif (is_object($group)) {
			$group_label = isset($group->label) ? $group->label : (isset($group->department) ? $group->department : '');
			$items = isset($group->items) && is_array($group->items) ? $group->items : array();
		}
		if ($group_label === '' || empty($items)) {
			continue;
		}
		$normalized_items = array();
		foreach ($items as $item) {
			$id = '';
			$text = '';
			$name_text = '';
			$meta = '';
			if (is_array($item)) {
				$id = isset($item['id']) ? $item['id'] : '';
				$text = isset($item['text']) ? $item['text'] : '';
				$name_text = isset($item['name']) ? $item['name'] : $text;
				$meta = isset($item['meta']) ? $item['meta'] : (isset($item['designation']) ? $item['designation'] : '');
			} elseif (is_object($item)) {
				$id = isset($item->id) ? $item->id : '';
				$text = isset($item->text) ? $item->text : '';
				$name_text = isset($item->name) ? $item->name : $text;
				$meta = isset($item->meta) ? $item->meta : (isset($item->designation) ? $item->designation : '');
			}
			if ($id === '' || $text === '') {
				continue;
			}
			$normalized_items[] = array(
				'id' => (string) $id,
				'text' => (string) $text,
				'name' => (string) $name_text,
				'meta' => (string) $meta,
			);
		}
		if (!empty($normalized_items)) {
			$normalized_groups[] = array(
				'label' => (string) $group_label,
				'items' => $normalized_items,
			);
		}
	}

	$wrapper_attrs = array(
		'class' => trim('erp-grouped-lookup ' . $wrapper_class . ($layout === 'horizontal' ? '' : ' space-y-2')),
		'data-grouped-lookup' => '1',
	);
	$wrapper_attr_string = build_html_attributes($wrapper_attrs);
	$input_attrs = array(
		'id' => $input_id,
		'type' => 'text',
		'value' => $selected_label,
		'autocomplete' => 'off',
		'placeholder' => $placeholder,
			'class' => trim($input_classes . ' erp-grouped-lookup__input'),
			'data-grouped-lookup-input' => '1',
		);
	$hidden_attrs = array(
		'type' => 'hidden',
		'name' => $name,
		'value' => $selected_id,
		'data-grouped-lookup-hidden' => '1',
	);


	$input_shell = '<div class="erp-grouped-lookup__shell space-y-0" data-grouped-lookup-root>'
			. '<input ' . build_html_attributes($hidden_attrs) . '>'
			. '<div class="relative">'
			. '<span class="erp-grouped-lookup__icon pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3 text-slate-400 dark:text-slate-500"><i data-lucide="search" class="h-4 w-4"></i></span>'
			. '<input ' . build_html_attributes($input_attrs) . '>'
			. '<button type="button" class="erp-grouped-lookup__clear absolute right-2.5 top-1/2 hidden -translate-y-1/2 items-center justify-center rounded-full p-1 text-slate-400 transition hover:bg-slate-100 hover:text-slate-700 dark:text-slate-500 dark:hover:bg-slate-800 dark:hover:text-slate-200" data-grouped-lookup-clear><i data-lucide="x" class="h-3.5 w-3.5"></i></button>'
			. '<div class="erp-grouped-lookup__results absolute left-0 top-full z-30 mt-2 hidden max-h-[18rem] w-full overflow-y-auto rounded-[1.1rem] border border-slate-200 bg-white p-1 shadow-xl shadow-slate-950/10 backdrop-blur-sm dark:border-slate-800 dark:bg-slate-950/95 dark:shadow-black/30" data-grouped-lookup-results></div>'
			. '</div>'
			. '<script type="application/json" data-grouped-lookup-source>' . json_encode($normalized_groups, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '</script>'
			. '</div>';
	$field_html = $input_shell;
	if (!empty($append_html)) {
		$field_html = '<div class="flex items-stretch">'
			. '<div class="min-w-0 flex-1">' . $input_shell . '</div>'
			. '<div class="shrink-0 flex items-center">' . $append_html . '</div>'
			. '</div>';
	}

	if ($layout === 'horizontal') {
		return '<div ' . $wrapper_attr_string . '>'
			. '<div class="flex items-start gap-3 py-2">'
			. '<label for="' . html_escape($input_id) . '" class="w-48 text-sm font-medium text-foreground pt-2">' . $label . $badge . '</label>'
			. '<div class="flex-1">'
			. $field_html
			. ($helper_text !== '' ? '<p class="text-xs text-muted-foreground">' . $helper_text . '</p>' : '')
			. '</div>'
			. '</div>'
			. '</div>';
	}

	return '<div ' . $wrapper_attr_string . '>'
		. '<label class="space-y-1 text-sm font-medium text-foreground block">'
		. '<span>' . $label . $badge . '</span>'
		. $field_html
		. '</label>'
		. ($helper_text !== '' ? '<p class="text-xs text-muted-foreground">' . $helper_text . '</p>' : '')
		. '</div>';
}

function grouped_staff_lookup_groups($include_clients = false)
{
	static $cache = array();
	$cache_key = $include_clients ? 'with_clients' : 'without_clients';
	if (isset($cache[$cache_key])) {
		return $cache[$cache_key];
	}

	$CI = &get_instance();
	$CI->db->select('tbl_users.user_id, tbl_account_details.fullname, tbl_designations.designations, tbl_departments.deptname');
	$CI->db->from('tbl_users');
	$CI->db->join('tbl_account_details', 'tbl_account_details.user_id = tbl_users.user_id', 'left');
	$CI->db->join('tbl_designations', 'tbl_designations.designations_id = tbl_account_details.designations_id', 'left');
	$CI->db->join('tbl_departments', 'tbl_departments.departments_id = tbl_designations.departments_id', 'left');
	if (!$include_clients) {
		$CI->db->where('tbl_users.role_id !=', 2);
	}
	$CI->db->where('tbl_account_details.fullname !=', '');
	$CI->db->order_by('tbl_departments.deptname', 'ASC');
	$CI->db->order_by('tbl_account_details.fullname', 'ASC');
	$rows = $CI->db->get()->result();

	$grouped = array();
	if (!empty($rows)) {
		foreach ($rows as $row) {
			$group_label = !empty($row->deptname) ? $row->deptname : lang('undefined_department');
			if (empty($grouped[$group_label])) {
				$grouped[$group_label] = array(
					'label' => $group_label,
					'items' => array(),
				);
			}
			$text = $row->fullname;
			$grouped[$group_label]['items'][] = array(
				'id' => (string) $row->user_id,
				'name' => (string) $row->fullname,
				'display' => (string) $row->fullname,
				'meta' => (string) (!empty($row->designations) ? $row->designations : ''),
				'text' => (string) $text,
			);
		}
	}

	$cache[$cache_key] = array_values($grouped);
	return $cache[$cache_key];
}

function normalize_lookup_selected_items($selected)
{
	$items = array();
	if (empty($selected)) {
		return $items;
	}

	foreach ($selected as $key => $value) {
		$id = '';
		$text = '';
		if (is_array($value)) {
			$id = $value['id'] ?? $value['value'] ?? $key;
			$text = $value['text'] ?? $value['label'] ?? $value['name'] ?? '';
		} elseif (is_object($value)) {
			$id = $value->id ?? $value->value ?? $key;
			$text = $value->text ?? $value->label ?? $value->name ?? '';
		} else {
			$id = is_string($key) || is_int($key) ? $key : $value;
			$text = $value;
		}

		if ($id === '' || $text === '') {
			continue;
		}

		$items[] = array(
			'id' => (string) $id,
			'text' => (string) $text,
		);
	}

	return $items;
}

function form_multi_lookup_group($name, $label, array $selected = array(), $required = false, array $attrs = array(), $wrapper_class = 'md:col-span-2', array $config = array(), $layout = 'stacked')
{
	$type = !empty($config['type']) ? trim((string) $config['type']) : preg_replace('/_ids?\[\]$/', '', preg_replace('/_ids?$/', '', $name));
	if ($type === '') {
		$type = 'project';
	}

	$input_id = isset($attrs['id']) ? $attrs['id'] : preg_replace('/[^a-z0-9_]/i', '_', $name) . '_lookup_multi';
	$input_classes = 'w-full rounded-lg border border-border bg-input px-3 py-2 text-sm dark:focus:border-secondary dark:focus:ring-3 dark:focus:ring-secondary/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-secondary/30';
	if (!empty($attrs['class'])) {
		$input_classes .= ' ' . $attrs['class'];
	}

	$placeholder = !empty($config['placeholder'])
		? $config['placeholder']
		: (lang('search') . ' ' . $label);

	$input_attrs = array_merge($attrs, array(
		'id' => $input_id,
		'type' => 'text',
		'class' => $input_classes,
		'autocomplete' => 'off',
		'data-lookup-input' => '1',
		'placeholder' => $placeholder,
	));
	$input_attr_string = build_html_attributes($input_attrs);
	$badge = $required ? ' <span class="text-red-500">*</span>' : '';
	$wrapper_classes = trim($wrapper_class . ($layout === 'horizontal' ? '' : ' space-y-1'));
	$wrapper_attrs = array(
		'class' => $wrapper_classes,
		'data-lookup' => '1',
		'data-lookup-type' => $type,
		'data-lookup-multiple' => '1',
		'data-lookup-name' => preg_match('/\[\]$/', $name) ? $name : ($name . '[]'),
		'data-lookup-limit' => array_key_exists('limit', $config) ? (int) $config['limit'] : 4,
		'data-lookup-min-length' => array_key_exists('min_length', $config) ? (int) $config['min_length'] : 0,
	);
	if (!empty($config['endpoint'])) {
		$wrapper_attrs['data-lookup-endpoint'] = $config['endpoint'];
	}
	if (!empty($config['onchange'])) {
		$wrapper_attrs['data-lookup-onchange'] = $config['onchange'];
	}
	if (!empty($config['depends'])) {
		$wrapper_attrs['data-lookup-depends'] = $config['depends'];
	}
	if (!empty($config['extra'])) {
		$wrapper_attrs['data-lookup-extra'] = json_encode($config['extra']);
	}
	$wrapper_attr_string = build_html_attributes($wrapper_attrs);
	$selected_items = normalize_lookup_selected_items($selected);
	$chips_html = '';
	$hidden_inputs = '';
	foreach ($selected_items as $item) {
		$hidden_inputs .= '<input type="hidden" name="' . html_escape($wrapper_attrs['data-lookup-name']) . '" value="' . html_escape($item['id']) . '" data-lookup-hidden-item>';
		$chips_html .= '<span class="inline-flex items-center gap-1 rounded-full border border-border bg-background px-3 py-1 text-xs font-medium text-foreground" data-lookup-chip data-lookup-chip-id="' . html_escape($item['id']) . '">'
			. '<span>' . html_escape($item['text']) . '</span>'
			. '<button type="button" class="rounded-full p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground" data-lookup-chip-remove="' . html_escape($item['id']) . '">'
			. '<i data-lucide="x" class="h-3 w-3"></i>'
			. '</button>'
			. '</span>';
	}
	$results = '<div class="absolute z-30 mt-1 w-full rounded-lg border border-border bg-card shadow-lg hidden max-h-56 overflow-y-auto" data-lookup-results></div>';

	if ($layout === 'horizontal') {
		return '<div ' . $wrapper_attr_string . '>'
			. '<div class="flex items-start gap-3 py-2">'
			. '<label for="' . html_escape($input_id) . '" class="w-48 text-sm font-medium text-foreground pt-2">' . $label . $badge . '</label>'
			. '<div class="flex-1">'
			. '<div data-lookup-hidden-list>' . $hidden_inputs . '</div>'
			. '<div class="mb-2 flex flex-wrap gap-2" data-lookup-chips>' . $chips_html . '</div>'
			. '<div class="relative">'
			. '<input ' . $input_attr_string . '>'
			. $results
			. '</div>'
			. '<p class="text-xs text-rose-500 hidden" data-lookup-status>' . $placeholder . '</p>'
			. '</div>'
			. '</div>'
			. '</div>';
	}

	return '<div ' . $wrapper_attr_string . '>'
		. '<label class="space-y-1 text-sm font-medium text-foreground block">'
		. '<span>' . $label . $badge . '</span>'
		. '<div data-lookup-hidden-list>' . $hidden_inputs . '</div>'
		. '<div class="mb-2 flex flex-wrap gap-2" data-lookup-chips>' . $chips_html . '</div>'
		. '<div class="relative">'
		. '<input ' . $input_attr_string . '>'
		. $results
		. '</div>'
		. '</label>'
		. '<p class="text-xs text-rose-500 hidden" data-lookup-status>' . $placeholder . '</p>'
		. '</div>';
}

/**
 * Project lookup input (dynamic search + hidden id).
 */
function form_project_lookup_group($name, $label, $selected_id = '', $selected_label = '', $required = false, array $attrs = array(), $wrapper_class = 'md:col-span-2')
{
	$config = array(
		'type' => 'project',
		'onchange' => 'get_milestone_by_id',
	);

	if (array_key_exists('data-lookup-onchange', $attrs)) {
		$config['onchange'] = $attrs['data-lookup-onchange'];
		unset($attrs['data-lookup-onchange']);
	}
	if (array_key_exists('data-lookup-depends', $attrs)) {
		$config['depends'] = $attrs['data-lookup-depends'];
		unset($attrs['data-lookup-depends']);
	}
	if (array_key_exists('data-lookup-endpoint', $attrs)) {
		$config['endpoint'] = $attrs['data-lookup-endpoint'];
		unset($attrs['data-lookup-endpoint']);
	}
	if (array_key_exists('data-lookup-limit', $attrs)) {
		$config['limit'] = $attrs['data-lookup-limit'];
		unset($attrs['data-lookup-limit']);
	}
	if (array_key_exists('data-lookup-min-length', $attrs)) {
		$config['min_length'] = $attrs['data-lookup-min-length'];
		unset($attrs['data-lookup-min-length']);
	}

	if (array_key_exists('data-lookup-extra', $attrs)) {
		$config['extra'] = $attrs['data-lookup-extra'];
		unset($attrs['data-lookup-extra']);
	}

	return form_lookup_group($name, $label, $selected_id, $selected_label, $required, $attrs, $wrapper_class, $config);
}

/**
 * Generic dropdown lookup (dynamic search + hidden id).
 */
function form_dropdown_lookup_group($id, $label, $onchangeFilter = '', array $attrs = array(), $wrapper_class = '', $position = 'right-0')
{
	$icon = isset($attrs['icon']) ? $attrs['icon'] : 'search';
	$type = isset($attrs['type']) ? $attrs['type'] : html_escape($id);
	$wrapper_attrs = array(
		'class' => trim($wrapper_class . ' space-y-1'),
		'data-lookup' => '1',
		'data-lookup-type' => $type,
		'data-lookup-onchange' => $onchangeFilter,
		'data-lookup-limit' => isset($attrs['limit']) ? (int) $attrs['limit'] : 4,
		'data-lookup-min-length' => isset($attrs['min_length']) ? (int) $attrs['min_length'] : 0,
	);

	if (isset($attrs['endpoint'])) {
		$wrapper_attrs['data-lookup-endpoint'] = $attrs['endpoint'];
	}
	if (!empty($attrs['extra'])) {
		$wrapper_attrs['data-lookup-extra'] = json_encode($attrs['extra']);
	}
	$wrapper_attr_string = build_html_attributes($wrapper_attrs);
	$id = html_escape($id) . '_lookup_dropdown';
	$label = html_escape($label);
	$html = '<div class="relative" data-dropdown>
						<button type="button" data-dropdown-toggle="' . ($id) . '"
							class="inline-flex items-center gap-2 rounded-lg border border-border bg-card px-3 py-2 text-sm font-semibold text-foreground hover:border-primary">
							<i data-lucide="' . html_escape($icon) . '" class="h-3.5 w-3.5"></i>'
		. $label .
		'</button>
							<div id="' . ($id) . '" data-dropdown-menu data-dropdown-preferred="' . (strpos((string) $position, 'left-') !== false ? 'left' : 'right') . '" class="hidden absolute ' . $position . ' mt-2 w-80 rounded-xl border border-border bg-card shadow-lg p-3 space-y-3 z-30">
							<div ' . $wrapper_attr_string . '>
								<input type="hidden" data-lookup-hidden>
								<div class="relative">
									' . form_input_group($id . '_search', $label, '', 'text', false, array('data-lookup-input' => true, 'autocomplete' => 'off')) . '
									<div class="max-h-48 overflow-y-auto rounded-lg border border-border bg-card hidden mt-2" data-lookup-results></div>
								</div>
								<div class="flex justify-end">
									<button type="button" class="hidden inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground" data-lookup-clear>
										<i data-lucide="x" class="h-3.5 w-3.5"></i>Clear
									</button>
								</div>
								<p class="text-xs text-rose-500 hidden" data-lookup-status>' . $label . '</p>
							</div>
						</div>
					</div>';
	return $html;
}

/**
 * Related-to selector + dynamic field container.
 * Provide types map and field definitions for the selected type.
 */
function form_related_to_block(array $config = array())
{
	$defaults = array(
		'label' => lang('related_to'),
		'name' => 'related_to',
		'select_id' => 'check_related',
		'container_id' => 'related_to',
		'selected_type' => '0',
		'onchange' => 'get_related_moduleName(this.value,null,4)',
		'types' => array(), // type => label
		'fields' => array(), // type => field config
		'extras' => array(), // type => array of field configs
		'attrs' => array(),
	);
	$cfg = array_replace($defaults, $config);

	if (empty($cfg['types']) && !empty($cfg['fields'])) {
		foreach ($cfg['fields'] as $key => $field) {
			if (!isset($cfg['types'][$key]) && !empty($field['label'])) {
				$cfg['types'][$key] = $field['label'];
			}
		}
	}

	if (empty($cfg['types'])) {
		$cfg['types'] = array(
			'project' => lang('project'),
			'opportunities' => lang('opportunities'),
			'leads' => lang('leads'),
			'bug' => lang('bugs'),
			'goal' => lang('goal_tracking'),
			'sub_task' => lang('tasks'),
			'expenses' => lang('expenses'),
		);
	}

	$select_options = array('0' => lang('none'));
	foreach ($cfg['types'] as $type => $label) {
		$select_options[$type] = $label;
	}

	$select_attrs = array_merge($cfg['attrs'], array(
		'id' => $cfg['select_id'],
		'onchange' => $cfg['onchange'],
	));

	$select_html = form_select_group($cfg['name'], $cfg['label'], $select_options, $cfg['selected_type'], false, $select_attrs);

	$detail_html = '';
	$selected_type = $cfg['selected_type'];
	if (!empty($selected_type) && $selected_type !== '0' && !empty($cfg['fields'][$selected_type])) {
		$detail_html .= render_related_field($cfg['fields'][$selected_type]);
		if (!empty($cfg['extras'][$selected_type]) && is_array($cfg['extras'][$selected_type])) {
			foreach ($cfg['extras'][$selected_type] as $extra_field) {
				$detail_html .= render_related_field($extra_field);
			}
		}
	}

	return '<div class="space-y-2">'
		. $select_html
		. '<div id="' . html_escape($cfg['container_id']) . '" class="">' . $detail_html . '</div>'
		. '</div>';
}

function render_related_field(array $field)
{
	if (!empty($field['html'])) {
		return $field['html'];
	}

	$name = !empty($field['name']) ? $field['name'] : '';
	$label = !empty($field['label']) ? $field['label'] : '';
	$options = !empty($field['options']) ? $field['options'] : array();
	$selected = isset($field['selected']) ? $field['selected'] : '';
	$required = !empty($field['required']);
	$attrs = !empty($field['attrs']) ? $field['attrs'] : array();
	$wrapper_class = !empty($field['wrapper_class']) ? $field['wrapper_class'] : 'md:col-span-2';

	if (empty($name)) {
		return '';
	}

	return '<div class="' . html_escape($wrapper_class) . '">'
		. form_select_group($name, $label, $options, $selected, $required, $attrs)
		. '</div>';
}

/**
 * Auto builder for related-to block with minimal config.
 */
function form_related_to_block_auto(array $config = array())
{
	$defaults = array(
		'label' => lang('related_to'),
		'name' => 'related_to',
		'select_id' => 'check_related',
		'container_id' => 'related_to',
		'selected_type' => '',
		'selected' => array(), // type => id
		'types' => array('project', 'opportunities', 'leads', 'bug', 'goal', 'sub_task', 'expenses'),
		'labels' => array(),
		'attrs' => array(),
		'onchange' => 'get_related_moduleName(this.value,null,4)',
	);
	$cfg = array_replace($defaults, $config);
	$selected = is_array($cfg['selected']) ? $cfg['selected'] : array();

	$type_labels = array(
		'project' => lang('project'),
		'opportunities' => lang('opportunities'),
		'leads' => lang('leads'),
		'bug' => lang('bugs'),
		'goal' => lang('goal_tracking'),
		'sub_task' => lang('tasks'),
		'expenses' => lang('expenses'),
	);
	if (!empty($cfg['labels'])) {
		$type_labels = array_merge($type_labels, $cfg['labels']);
	}

	$selected_type = $cfg['selected_type'];
	if (empty($selected_type)) {
		foreach ($cfg['types'] as $type) {
			if (!empty($selected[$type])) {
				$selected_type = $type;
				break;
			}
		}
	}
	if (empty($selected_type)) {
		$selected_type = '0';
	}

	$fields = array();
	$extras = array();

	if ($selected_type !== '0') {
		$CI = &get_instance();
		if (!isset($CI->admin_model)) {
			$CI->load->model('admin_model');
		}

		if ($selected_type === 'project') {
			$selected_project_id = isset($selected['project']) ? $selected['project'] : '';
			$selected_project_label = '';
			if (!empty($selected_project_id)) {
				$project_row = $CI->db->select('project_name')->where('project_id', $selected_project_id)->get('tbl_project')->row();
				if (!empty($project_row)) {
					$selected_project_label = $project_row->project_name;
				}
			}
			$fields['project'] = array(
				'html' => form_project_lookup_group(
					'project_id',
					$type_labels['project'],
					$selected_project_id,
					$selected_project_label,
					true,
					array('class' => 'project_module', 'data-lookup-onchange' => '')
				),
			);
			$milestone_options = array('' => lang('none'));
			if (!empty($selected['project'])) {
				$all_milestones = $CI->db->where('project_id', $selected['project'])->get('tbl_milestones')->result();
				if (!empty($all_milestones)) {
					foreach ($all_milestones as $v_milestone) {
						$milestone_options[$v_milestone->milestones_id] = $v_milestone->milestone_name;
					}
				}
			}
			$extras['project'][] = array(
				'html' => form_lookup_group(
					'milestones_id',
					lang('milestones'),
					isset($selected['milestones_id']) ? $selected['milestones_id'] : '',
					isset($milestone_options[$selected['milestones_id'] ?? '']) ? $milestone_options[$selected['milestones_id'] ?? ''] : '',
					false,
					array(),
					'md:col-span-2',
					array(
						'type' => 'milestone',
						'depends' => 'project_id',
						'min_length' => 0,
					)
				),
			);
		} elseif ($selected_type === 'opportunities') {
			$rows = $CI->admin_model->get_permission('tbl_opportunities');
			$options = array();
			if (!empty($rows)) {
				foreach ($rows as $row) {
					$options[$row->opportunities_id] = $row->opportunity_name;
				}
			}
			$fields['opportunities'] = array(
				'name' => 'opportunities_id',
				'label' => lang('select') . ' ' . $type_labels['opportunities'],
				'options' => $options,
				'selected' => isset($selected['opportunities']) ? $selected['opportunities'] : '',
				'required' => true,
				'attrs' => array('class' => 'select_box opportunities_module'),
			);
		} elseif ($selected_type === 'leads') {
			$rows = $CI->admin_model->get_permission('tbl_leads');
			$options = array();
			if (!empty($rows)) {
				foreach ($rows as $row) {
					$options[$row->leads_id] = $row->lead_name;
				}
			}
			$fields['leads'] = array(
				'name' => 'leads_id',
				'label' => lang('select') . ' ' . $type_labels['leads'],
				'options' => $options,
				'selected' => isset($selected['leads']) ? $selected['leads'] : '',
				'required' => true,
				'attrs' => array('class' => 'select_box leads_module'),
			);
		} elseif ($selected_type === 'bug') {
			$rows = $CI->admin_model->get_permission('tbl_bug');
			$options = array();
			if (!empty($rows)) {
				foreach ($rows as $row) {
					$options[$row->bug_id] = $row->bug_title;
				}
			}
			$fields['bug'] = array(
				'name' => 'bug_id',
				'label' => lang('select') . ' ' . $type_labels['bug'],
				'options' => $options,
				'selected' => isset($selected['bug']) ? $selected['bug'] : '',
				'required' => true,
				'attrs' => array('class' => 'select_box bugs_module'),
			);
		} elseif ($selected_type === 'goal') {
			$rows = $CI->admin_model->get_permission('tbl_goal_tracking');
			$options = array();
			if (!empty($rows)) {
				foreach ($rows as $row) {
					$options[$row->goal_tracking_id] = $row->subject;
				}
			}
			$fields['goal'] = array(
				'name' => 'goal_tracking_id',
				'label' => lang('select') . ' ' . $type_labels['goal'],
				'options' => $options,
				'selected' => isset($selected['goal']) ? $selected['goal'] : '',
				'required' => true,
				'attrs' => array('class' => 'select_box goal_tracking'),
			);
		} elseif ($selected_type === 'sub_task') {
			$rows = $CI->admin_model->get_permission('tbl_task');
			$options = array();
			if (!empty($rows)) {
				foreach ($rows as $row) {
					$options[$row->task_id] = $row->task_name;
				}
			}
			$fields['sub_task'] = array(
				'name' => 'sub_task_id',
				'label' => lang('select') . ' ' . $type_labels['sub_task'],
				'options' => $options,
				'selected' => isset($selected['sub_task']) ? $selected['sub_task'] : '',
				'required' => true,
				'attrs' => array('class' => 'select_box sub_tasks'),
			);
		} elseif ($selected_type === 'expenses') {
			$rows = $CI->admin_model->get_permission('tbl_transactions');
			$options = array();
			if (!empty($rows)) {
				foreach ($rows as $row) {
					$label = $row->name;
					if (!empty($row->reference)) {
						$label .= '#' . $row->reference;
					}
					$options[$row->transactions_id] = $label;
				}
			}
			$fields['expenses'] = array(
				'name' => 'transactions_id',
				'label' => lang('select') . ' ' . $type_labels['expenses'],
				'options' => $options,
				'selected' => isset($selected['expenses']) ? $selected['expenses'] : '',
				'required' => true,
				'attrs' => array('class' => 'select_box expenses'),
			);
		}
	}

	return form_related_to_block(array(
		'label' => $cfg['label'],
		'name' => $cfg['name'],
		'select_id' => $cfg['select_id'],
		'container_id' => $cfg['container_id'],
		'selected_type' => $selected_type,
		'types' => array_intersect_key($type_labels, array_flip($cfg['types'])),
		'fields' => $fields,
		'extras' => $extras,
		'attrs' => $cfg['attrs'],
		'onchange' => $cfg['onchange'],
	));
}

/**
 * Attachment UI block (dropzone, simple file, photo upload).
 */
function form_attachment_group($field, $label = null, $type = 'dropzone', array $config = [])
{
	$type = strtolower($type);

	if (!in_array($type, ['dropzone', 'file', 'photo'], true)) {
		$type = 'dropzone';
	}

	$defaults = [
		'heading'       => lang('attachments'),
		'dropzone_id'   => 'dz_' . $field,
		'list_id'       => 'dz_list_' . $field,
		'browse_label'  => lang('browse'),
		'no_file_label' => lang('no_file_selected'),
		'select_label'  => lang('select_image'),
		'remove_label'  => lang('remove'),
		'click_label'   => lang('file_upload_instruction'),
		'hint'          => '',
		'wrap'          => true,
		'comment'       => false,
		'preview_url'   => '',
		'existing_files' => array(),
		'download_url_base' => '',
	];

	$cfg = array_replace($defaults, $config);

	$input_name = 'name="' . html_escape($field) . '"';

	$label_html = '';
	if (!empty($label)) {
		$label_html = '<h5 class="text-sm font-medium text-foreground mb-1">'
			. html_escape($label) .
			'</h5>';
	}

	$block = '';

	/* =======================
       DROPZONE
       ======================= */
	if ($type === 'dropzone') {
		$existing_files_html = '';
		if (!empty($cfg['existing_files']) && (is_array($cfg['existing_files']) || $cfg['existing_files'] instanceof Traversable)) {
			foreach ($cfg['existing_files'] as $existing_key => $existing_file) {
				$existing_file = (array) $existing_file;
				$file_name = !empty($existing_file['fileName']) ? (string) $existing_file['fileName'] : '';
				$file_path = !empty($existing_file['path']) ? (string) $existing_file['path'] : '';
				if ($file_name === '' && $file_path === '') {
					continue;
				}
				$full_path = !empty($existing_file['fullPath']) ? (string) $existing_file['fullPath'] : '';
				$file_size = isset($existing_file['size']) ? (string) $existing_file['size'] : '';
				$is_image = !empty($existing_file['is_image']) ? '1' : '0';
				$download_url = !empty($cfg['download_url_base'])
					? rtrim((string) $cfg['download_url_base'], '/') . '/' . rawurlencode((string) $existing_key)
					: (!empty($file_path) ? base_url($file_path) : '#');
				$thumb_html = $is_image === '1' && $file_path !== ''
					? '<img src="' . html_escape(base_url($file_path)) . '" class="h-10 w-10 rounded-md object-cover ring-1 ring-slate-200 dark:ring-slate-600" alt="' . html_escape($file_name) . '">'
					: '<div class="flex h-10 w-10 items-center justify-center rounded-md bg-slate-200 text-[10px] font-bold text-slate-600 dark:bg-slate-700 dark:text-slate-200">FILE</div>';

				$existing_files_html .= '
				<div class="upload-row flex items-center gap-3 rounded-lg border border-slate-200 bg-slate-50 p-3 dark:border-slate-700 dark:bg-slate-700/50">
					<div class="flex-shrink-0">' . $thumb_html . '</div>
					<div class="min-w-0 flex-1">
						<a href="' . html_escape($download_url) . '" class="block truncate text-sm font-semibold text-indigo-600 hover:underline dark:text-indigo-300" target="_blank">
							' . html_escape($file_name ?: basename($file_path)) . '
						</a>
						' . ($file_size !== '' ? '<p class="mt-1 text-xs text-slate-500 dark:text-slate-400">' . html_escape($file_size) . ' ' . html_escape(lang('kb')) . '</p>' : '') . '
					</div>
					<button type="button" class="text-slate-400 transition-colors hover:text-red-500" title="' . html_escape(lang('remove')) . '" onclick="this.closest(\'.upload-row\').remove()">
						<i data-lucide="trash-2" class="h-4 w-4"></i>
					</button>
					<input type="hidden" name="fileName[]" value="' . html_escape($file_name) . '">
					<input type="hidden" name="path[]" value="' . html_escape($file_path) . '">
					<input type="hidden" name="fullPath[]" value="' . html_escape($full_path) . '">
					<input type="hidden" name="size[]" value="' . html_escape($file_size) . '">
					<input type="hidden" name="is_image[]" value="' . html_escape($is_image) . '">
				</div>';
			}
		}
		$block = '
        ' . $label_html . '
        <div id="' . html_escape($cfg['dropzone_id']) . '" 
             class="tw-dropzone relative border-2 border-dashed border-slate-300 dark:border-slate-600 rounded-lg p-6 flex flex-col items-center justify-center cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-700/50"
             data-list-target="#' . html_escape($cfg['list_id']) . '"' . (!empty($cfg['comment']) ? ' data-comment="true"' : '') . '>

            <input type="file" multiple class="absolute inset-0 opacity-0 cursor-pointer" ' . $input_name . '>

            <div class="text-center pointer-events-none">
                <p class="text-sm text-slate-700 dark:text-slate-200">
                    ' . ($cfg['click_label']) . '
                </p>
                ' . (!empty($cfg['hint']) ? '<p class="text-xs text-slate-400 mt-1">' . html_escape($cfg['hint']) . '</p>' : '') . '
            </div>
        </div>

        <div id="' . html_escape($cfg['list_id']) . '" class="mt-4 space-y-3">' . $existing_files_html . '</div>';
	}

	/* =======================
       SIMPLE FILE
       ======================= */
	if ($type === 'file') {
		$block = '
        ' . $label_html . '
        <div class="flex flex-wrap gap-1 2xl:gap-0" data-file-upload>
            <label class="bg-slate-100 dark:bg-slate-700 border border-r-0 border-slate-300 dark:border-slate-600 px-4 py-2 text-sm cursor-pointer rounded-l">
                ' . html_escape($cfg['browse_label']) . '
                <input type="file" class="hidden" ' . $input_name . '>
            </label>

            <input type="text" readonly
                placeholder="' . html_escape($cfg['no_file_label']) . '"
                class="flex-1 bg-white dark:bg-slate-900 border border-slate-300 dark:border-slate-600 px-3 py-2 text-sm rounded-r"
                data-file-target>
        </div>';
	}

	/* =======================
       PHOTO UPLOAD
       ======================= */
	if ($type === 'photo') {
		$preview_url = !empty($cfg['preview_url']) ? (string) $cfg['preview_url'] : '';
		$has_preview = $preview_url !== '';
		$block = '
        ' . $label_html . '
        <div data-photo-upload>
            <div class="photo-uploader-container cursor-pointer">
                <div class="photo-preview w-full h-48 bg-slate-100 dark:bg-slate-900 border-2 border-dashed border-slate-300 dark:border-slate-600 rounded-lg flex items-center justify-center mb-3 overflow-hidden">
                    <span data-photo-placeholder class="text-slate-400 text-sm' . ($has_preview ? ' hidden' : '') . '">
                        ' . html_escape($cfg['no_file_label']) . '
                    </span>
                    <img data-photo-img src="' . html_escape($preview_url) . '" class="' . ($has_preview ? '' : 'hidden ') . 'w-full h-full object-contain">
					<input type="file" accept="image/*" class="hidden" ' . $input_name . '>
                </div>

                <div class="flex gap-2">
                    <label class="bg-blue-600 text-white px-4 py-2 rounded text-sm cursor-pointer">
                        <span data-photo-btn-text>' . html_escape($has_preview ? lang('change') : $cfg['select_label']) . '</span>                        
                    </label>

                    <button type="button" data-photo-remove
                        class="hidden bg-red-100 text-red-600 px-4 py-2 rounded text-sm">
                        ' . html_escape($cfg['remove_label']) . '
                    </button>
                </div>
            </div>
        </div>';
	}

	if ($cfg['wrap'] === false) {
		return $block;
	}

	return '
    <div class=" mt-0">        
        ' . $block . '
    </div>';
}


/**
 * Attachment UI block by type (dropzone, simple, photo).
 */
function form_attachment($name = '', $label = '', $type = 'dropzone', array $attrs = array())
{
	return form_attachment_group($name, $label, $type, $attrs);
}

/**
 * Inline "add new" link for select fields (opens modal and appends option).
 */
function form_select_add_link($selectName, $url, $title, $size = 'md', array $attrs = array())
{
	$attrs['href'] = '#';
	$attrs['class'] = isset($attrs['class']) ? $attrs['class'] : 'text-xs group-hover:text-foreground dark:group-hover:text-gray-100 text-primary hover:underline';
	$attrs['data-modal-url'] = $url;
	$attrs['data-modal-title'] = $title;
	$attrs['data-modal-select'] = $selectName;
	$attrs['data-modal-size'] = $size;
	if (isset($attrs['data-modal-value-key']) === false) {
		$attrs['data-modal-value-key'] = 'id';
	}
	$attrString = build_html_attributes($attrs);
	return '<a data-tooltip="' . html_escape($title) . '" ' . $attrString . ' ><i data-lucide="plus" class="h-3.5 w-3.5 mr-0"></i></a>';
}

/**
 * Inline "add new" link for lookup fields (opens modal and updates lookup input).
 */
function form_lookup_add_link($lookupName, $url, $title, $size = 'md', array $attrs = array())
{
	$attrs['href'] = '#';
	$attrs['class'] = isset($attrs['class']) ? $attrs['class'] : 'text-xs group-hover:text-foreground dark:group-hover:text-gray-100 text-primary hover:underline';
	$attrs['data-modal-url'] = $url;
	$attrs['data-modal-title'] = $title;
	$attrs['data-modal-lookup'] = $lookupName;
	$attrs['data-modal-size'] = $size;
	if (isset($attrs['data-modal-value-key']) === false) {
		$attrs['data-modal-value-key'] = 'id';
	}
	$attrString = build_html_attributes($attrs);
	return '<a data-tooltip="' . html_escape($title) . '" ' . $attrString . ' ><i data-lucide="plus" class="h-3.5 w-3.5"></i></a>';
}

// btn_modal
function btn_modal($url, $label, $title, $size = 'md', $icon = 'plus',  array $attrs = array())
{
	$is_inline_modal = is_string($url) && strlen($url) > 1 && strpos($url, '#') === 0;
	$attrs['href'] = $is_inline_modal ? $url : '#';
	$attrs['class'] = isset($attrs['class']) ? $attrs['class'] . ' mb-0' : 'btn btn-default text-xs text-primary ml-1 w-6 h-6';
	if ($is_inline_modal) {
		$attrs['data-toggle'] = 'modal';
		$attrs['data-target'] = $url;
	} else {
		$attrs['data-modal-url'] = base_url($url);
		$attrs['data-modal-title'] = $title;
		$attrs['data-modal-size'] = $size;
	}
	$attrString = build_html_attributes($attrs);
	if ($icon) {
		$label = '<i data-lucide="' . $icon . '" class="h-3.5 w-3.5"></i> ' . $label;
	}
	return '<a data-tooltip="' . html_escape($title) . '" ' . $attrString . ' >' . $label . '</a>';
}


function form_datepicker($name, $label, $value = '', $required = false, array $attrs = array(),  $prepend = '', $append = '', $layout = 'stacked')
{
	$attrs['class'] = isset($attrs['class']) ? $attrs['class'] . ' ts-datepicker' : 'ts-datepicker';
	$attrs['data-type'] = isset($attrs['data-type']) ? $attrs['data-type'] : 'date';
	$attrs['data-format'] = isset($attrs['data-format']) ? $attrs['data-format'] : 'Y-m-d';
	$attrs['data-today-btn'] = isset($attrs['data-today-btn']) ? $attrs['data-today-btn'] : 'true';
	if (empty($attrs['autocomplete'])) {
		$attrs['autocomplete'] = 'off';
	}
	return form_input_group($name, $label, $value, 'text', $required, $attrs, $prepend, $append, $layout);
}

function form_timepicker($name, $label, $value = '', $required = false, array $attrs = array(), $layout = 'stacked', $prepend = '', $append = '')
{
	$attrs['class'] = isset($attrs['class']) ? $attrs['class'] . ' ts-datepicker' : 'ts-datepicker';
	$attrs['data-type'] = 'time';
	$attrs['data-format'] = isset($attrs['data-format']) ? $attrs['data-format'] : 'H:i';
	if (empty($attrs['autocomplete'])) {
		$attrs['autocomplete'] = 'off';
	}
	return form_input_group($name, $label, $value, 'text', $required, $attrs, $prepend, $append, $layout);
}

function form_colorpicker($name, $label, $value = '', $required = false, array $attrs = array(), $layout = 'stacked', $prepend = '', $append = '')
{
	$raw = (string) ($value ?: '#ffffff');
	if (preg_match('/#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})\b/', $raw, $m)) {
		$hex = $m[0];
	} else {
		$hex = '#ffffff';
	}
	// normalize 3-digit hex to 6-digit for native color input compatibility
	if (preg_match('/^#([0-9a-fA-F]{3})$/', $hex, $m3)) {
		$h = $m3[1];
		$hex = '#' . $h[0] . $h[0] . $h[1] . $h[1] . $h[2] . $h[2];
	}
	$attrs['class'] = isset($attrs['class']) ? trim($attrs['class'] . ' h-10') : 'h-10';
	unset($attrs['placeholder']);
	return form_input_group($name, $label, strtolower($hex), 'color', $required, $attrs, $prepend, $append, $layout);
}

function form_progress_slider($name, $label, $value = 0, $min = 0, $max = 100, array $attrs = array(), $layout = 'stacked')
{
	$id = isset($attrs['id']) ? $attrs['id'] : $name . '_slider';
	$display_id = $id . '_display';
	$hidden_id = $id . '_hidden';

	$range_attrs = $attrs;
	$range_attrs['type'] = 'range';
	$range_attrs['id'] = $id;
	$range_attrs['min'] = $min;
	$range_attrs['max'] = $max;
	$range_attrs['value'] = $value;
	$range_attrs['data-range-display'] = '#' . $display_id;
	$range_attrs['data-range-input'] = '#' . $hidden_id;
	$range_attrs['class'] = isset($range_attrs['class'])
		? $range_attrs['class'] . ' w-full focus:outline-none'
		: 'w-full focus:outline-none';
	$range_attr_string = build_html_attributes($range_attrs);

	$label_html = '<span>' . $label . '</span>';
	$range_html = '<div class="flex items-center gap-3">'
		. '<input ' . $range_attr_string . '>'
		. '<span id="' . html_escape($display_id) . '" class="text-sm font-semibold text-foreground">' . html_escape($value) . '%</span>'
		. '</div>'
		. '<input type="hidden" name="' . html_escape($name) . '" id="' . html_escape($hidden_id) . '" value="' . html_escape($value) . '">';

	if ($layout === 'horizontal') {
		return '<div class="flex items-start gap-3 py-2">'
			. '<label for="' . html_escape($id) . '" class="w-48 text-sm font-medium text-foreground pt-2">' . $label_html . '</label>'
			. '<div class="flex-1">' . $range_html . '</div>'
			. '</div>';
	}

	return '<label class="space-y-1 text-sm font-medium text-foreground block">'
		. $label_html
		. $range_html
		. '</label>';
}


function form_editor($name, $label, $value = '', $required = false, array $attrs = array(), $layout = 'stacked')
{
	$attrs['class'] = isset($attrs['class']) ? $attrs['class'] . ' tw-editor' : 'tw-editor';
	$attrs['rows'] = isset($attrs['rows']) ? $attrs['rows'] : 5;
	return form_textarea_group($name, $label, $value, $attrs['rows'], $required, $attrs, $layout, '', '', true);
}


if (!function_exists('mb_convert_encoding')) {

	function mb_convert_encoding($string, $to, $from)
	{
		return $string;
	}
}

if (!function_exists('mb_list_encodings')) {

	function mb_list_encodings()
	{
		return array('UTF-8');
	}
}
if (!function_exists('mb_strtolower')) {

	function mb_strtolower($string)
	{
		// convert to lowercase and remove all non-ASCII characters from the string (leaving accented characters)
		$string = strtolower(preg_replace('/[^a-zA-Z0-9\s]/', '', $string));
		return $string;
	}
}


function slug_it($str, $options = array())
{
	// Make sure string is in UTF-8 and strip invalid UTF-8 characters
	$str = mb_convert_encoding((string)$str, 'UTF-8', mb_list_encodings());
	$defaults = array(
		'delimiter' => '_',
		'limit' => null,
		'lowercase' => true,
		'replacements' => array(
			'
            /\b(Ñ“)\b/i' => 'gj',
			'/\b(Ñ‡)\b/i' => 'ch',
			'/\b(Ñˆ)\b/i' => 'sh',
			'/\b(Ñ™)\b/i' => 'lj'
		),
		'transliterate' => true
	);
	// Merge options
	if (!is_array($options)) {
		$options = array($options);
	}
	$options = array_merge($defaults, $options);
	$char_map = array(
		// Latin
		'Ã€' => 'A',
		'Ã' => 'A',
		'Ã‚' => 'A',
		'Ãƒ' => 'A',
		'Ã„' => 'A',
		'Ã…' => 'A',
		'Ã†' => 'AE',
		'Ã‡' => 'C',
		'Ãˆ' => 'E',
		'Ã‰' => 'E',
		'ÃŠ' => 'E',
		'Ã‹' => 'E',
		'ÃŒ' => 'I',
		'Ã' => 'I',
		'ÃŽ' => 'I',
		'Ã' => 'I',
		'Ã' => 'D',
		'Ã‘' => 'N',
		'Ã’' => 'O',
		'Ã“' => 'O',
		'Ã”' => 'O',
		'Ã•' => 'O',
		'Ã–' => 'O',
		'Å' => 'O',
		'Ã˜' => 'O',
		'Ã™' => 'U',
		'Ãš' => 'U',
		'Ã›' => 'U',
		'Ãœ' => 'U',
		'Å°' => 'U',
		'Ã' => 'Y',
		'Ãž' => 'TH',
		'ÃŸ' => 'ss',
		'Ã ' => 'a',
		'Ã¡' => 'a',
		'Ã¢' => 'a',
		'Ã£' => 'a',
		'Ã¤' => 'a',
		'Ã¥' => 'a',
		'Ã¦' => 'ae',
		'Ã§' => 'c',
		'Ã¨' => 'e',
		'Ã©' => 'e',
		'Ãª' => 'e',
		'Ã«' => 'e',
		'Ã¬' => 'i',
		'Ã­' => 'i',
		'Ã®' => 'i',
		'Ã¯' => 'i',
		'Ã°' => 'd',
		'Ã±' => 'n',
		'Ã²' => 'o',
		'Ã³' => 'o',
		'Ã´' => 'o',
		'Ãµ' => 'o',
		'Ã¶' => 'o',
		'Å‘' => 'o',
		'Ã¸' => 'o',
		'Ã¹' => 'u',
		'Ãº' => 'u',
		'Ã»' => 'u',
		'Ã¼' => 'u',
		'Å±' => 'u',
		'Ã½' => 'y',
		'Ã¾' => 'th',
		'Ã¿' => 'y',
		// Latin symbols
		'Â©' => '(c)',
		// Greek
		'Î‘' => 'A',
		'Î’' => 'B',
		'Î“' => 'G',
		'Î”' => 'D',
		'Î•' => 'E',
		'Î–' => 'Z',
		'Î—' => 'H',
		'Î˜' => '8',
		'Î™' => 'I',
		'Îš' => 'K',
		'Î›' => 'L',
		'Îœ' => 'M',
		'Î' => 'N',
		'Îž' => '3',
		'ÎŸ' => 'O',
		'Î ' => 'P',
		'Î¡' => 'R',
		'Î£' => 'S',
		'Î¤' => 'T',
		'Î¥' => 'Y',
		'Î¦' => 'F',
		'Î§' => 'X',
		'Î¨' => 'PS',
		'Î©' => 'W',
		'Î†' => 'A',
		'Îˆ' => 'E',
		'ÎŠ' => 'I',
		'ÎŒ' => 'O',
		'ÎŽ' => 'Y',
		'Î‰' => 'H',
		'Î' => 'W',
		'Îª' => 'I',
		'Î«' => 'Y',
		'Î±' => 'a',
		'Î²' => 'b',
		'Î³' => 'g',
		'Î´' => 'd',
		'Îµ' => 'e',
		'Î¶' => 'z',
		'Î·' => 'h',
		'Î¸' => '8',
		'Î¹' => 'i',
		'Îº' => 'k',
		'Î»' => 'l',
		'Î¼' => 'm',
		'Î½' => 'n',
		'Î¾' => '3',
		'Î¿' => 'o',
		'Ï€' => 'p',
		'Ï' => 'r',
		'Ïƒ' => 's',
		'Ï„' => 't',
		'Ï…' => 'y',
		'Ï†' => 'f',
		'Ï‡' => 'x',
		'Ïˆ' => 'ps',
		'Ï‰' => 'w',
		'Î¬' => 'a',
		'Î­' => 'e',
		'Î¯' => 'i',
		'ÏŒ' => 'o',
		'Ï' => 'y',
		'Î®' => 'h',
		'ÏŽ' => 'w',
		'Ï‚' => 's',
		'ÏŠ' => 'i',
		'Î°' => 'y',
		'Ï‹' => 'y',
		'Î' => 'i',
		// Turkish
		'Åž' => 'S',
		'Ä°' => 'I',
		'Ã‡' => 'C',
		'Ãœ' => 'U',
		'Ã–' => 'O',
		'Äž' => 'G',
		'ÅŸ' => 's',
		'Ä±' => 'i',
		'Ã§' => 'c',
		'Ã¼' => 'u',
		'Ã¶' => 'o',
		'ÄŸ' => 'g',
		// Russian
		'Ð' => 'A',
		'Ð‘' => 'B',
		'Ð’' => 'V',
		'Ð“' => 'G',
		'Ð”' => 'D',
		'Ð•' => 'E',
		'Ð' => 'Yo',
		'Ð–' => 'Zh',
		'Ð—' => 'Z',
		'Ð˜' => 'I',
		'Ð™' => 'J',
		'Ðš' => 'K',
		'Ð›' => 'L',
		'Ðœ' => 'M',
		'Ð' => 'N',
		'Ðž' => 'O',
		'ÐŸ' => 'P',
		'Ð ' => 'R',
		'Ð¡' => 'S',
		'Ð¢' => 'T',
		'Ð£' => 'U',
		'Ð¤' => 'F',
		'Ð¥' => 'H',
		'Ð¦' => 'C',
		'Ð§' => 'Ch',
		'Ð¨' => 'Sh',
		'Ð©' => 'Sh',
		'Ðª' => '',
		'Ð«' => 'Y',
		'Ð¬' => '',
		'Ð­' => 'E',
		'Ð®' => 'Yu',
		'Ð¯' => 'Ya',
		'Ð°' => 'a',
		'Ð±' => 'b',
		'Ð²' => 'v',
		'Ð³' => 'g',
		'Ð´' => 'd',
		'Ðµ' => 'e',
		'Ñ‘' => 'yo',
		'Ð¶' => 'zh',
		'Ð·' => 'z',
		'Ð¸' => 'i',
		'Ð¹' => 'j',
		'Ðº' => 'k',
		'Ð»' => 'l',
		'Ð¼' => 'm',
		'Ð½' => 'n',
		'Ð¾' => 'o',
		'Ð¿' => 'p',
		'Ñ€' => 'r',
		'Ñ' => 's',
		'Ñ‚' => 't',
		'Ñƒ' => 'u',
		'Ñ„' => 'f',
		'Ñ…' => 'h',
		'Ñ†' => 'c',
		'Ñ‡' => 'ch',
		'Ñˆ' => 'sh',
		'Ñ‰' => 'sh',
		'ÑŠ' => '',
		'Ñ‹' => 'y',
		'ÑŒ' => '',
		'Ñ' => 'e',
		'ÑŽ' => 'yu',
		'Ñ' => 'ya',
		// Ukrainian
		'Ð„' => 'Ye',
		'Ð†' => 'I',
		'Ð‡' => 'Yi',
		'Ò' => 'G',
		'Ñ”' => 'ye',
		'Ñ–' => 'i',
		'Ñ—' => 'yi',
		'Ò‘' => 'g',
		// Czech
		'ÄŒ' => 'C',
		'ÄŽ' => 'D',
		'Äš' => 'E',
		'Å‡' => 'N',
		'Å˜' => 'R',
		'Å ' => 'S',
		'Å¤' => 'T',
		'Å®' => 'U',
		'Å½' => 'Z',
		'Ä' => 'c',
		'Ä' => 'd',
		'Ä›' => 'e',
		'Åˆ' => 'n',
		'Å™' => 'r',
		'Å¡' => 's',
		'Å¥' => 't',
		'Å¯' => 'u',
		'Å¾' => 'z',
		// Polish
		'Ä„' => 'A',
		'Ä†' => 'C',
		'Ä˜' => 'e',
		'Å' => 'L',
		'Åƒ' => 'N',
		'Ã“' => 'o',
		'Åš' => 'S',
		'Å¹' => 'Z',
		'Å»' => 'Z',
		'Ä…' => 'a',
		'Ä‡' => 'c',
		'Ä™' => 'e',
		'Å‚' => 'l',
		'Å„' => 'n',
		'Ã³' => 'o',
		'Å›' => 's',
		'Åº' => 'z',
		'Å¼' => 'z',
		// Latvian
		'Ä€' => 'A',
		'ÄŒ' => 'C',
		'Ä’' => 'E',
		'Ä¢' => 'G',
		'Äª' => 'i',
		'Ä¶' => 'k',
		'Ä»' => 'L',
		'Å…' => 'N',
		'Å ' => 'S',
		'Åª' => 'u',
		'Å½' => 'Z',
		'Ä' => 'a',
		'Ä' => 'c',
		'Ä“' => 'e',
		'Ä£' => 'g',
		'Ä«' => 'i',
		'Ä·' => 'k',
		'Ä¼' => 'l',
		'Å†' => 'n',
		'Å¡' => 's',
		'Å«' => 'u',
		'Å¾' => 'z',
	);

	// Make custom replacements
	$str = preg_replace(array_keys($options['replacements']), $options['replacements'], $str);
	// Transliterate characters to ASCII
	if ($options['transliterate']) {
		$str = str_replace(array_keys($char_map), $char_map, $str);
	}
	// Replace non-alphanumeric characters with our delimiter
	$str = preg_replace('/[^\p{L}\p{Nd}]+/u', $options['delimiter'], $str);
	// Remove duplicate delimiters
	$str = preg_replace('/(' . preg_quote($options['delimiter'], '/') . '){2,}/', '$1', $str);
	// Truncate slug to max. characters
	$str = mb_substr($str, 0, ($options['limit'] ? $options['limit'] : mb_strlen($str, 'UTF-8')), 'UTF-8');
	// Remove delimiter from ends
	$str = trim($str, $options['delimiter']);
	return $options['lowercase'] ? mb_strtolower($str, 'UTF-8') : $str;
}

function display_money($value, $currency = false, $decimal = 2)
{
	$value = is_numeric($value) ? (float) $value : 0.0;
	if (!empty(config_item('decimal_separator'))) {
		$decimal = config_item('decimal_separator');
	}
	switch (config_item('money_format')) {
		case 1:
			$value = number_format($value, $decimal, '.', ',');
			break;
		case 2:
			$value = number_format($value, $decimal, ',', '.');
			break;
		case 3:
			$value = number_format($value, $decimal, '.', '');
			break;
		case 4:
			$value = number_format($value, $decimal, ',', '');
			break;
		case 5:
			$value = number_format($value, $decimal, ".", "'");
			break;
		case 6:
			$value = number_format($value, $decimal, ".", " ");
			break;
		case 7:
			$value = number_format($value, $decimal, ",", " ");
			break;
		case 8:
			$value = number_format($value, $decimal, "'", " ");
			break;
		default:
			$value = number_format($value, $decimal, '.', ',');
			break;
	}
	switch (config_item('currency_position')) {
		case 1:
			$return = $currency . ' ' . $value;
			break;
		case 2:
			$return = $value . ' ' . $currency;
			break;
		case false:
			$return = $value;
			break;
		default:
			$return = $currency . ' ' . $value;
			break;
	}

	return $return;
}

function display_time($value, $no_str = null)
{
	if (!empty($no_str)) {
		$time = $value;
	} else {
		$time = safe_strtotime($value);
	}
	return ci_format_datetime($time, config_item('time_format'), true);
}
function display_date($value)
{
	if (empty($value)) {
		return null;
	}

	return ci_format_datetime($value, config_item('date_format'));
}

function display_datetime($value, $no_str = null)
{
	if (empty($value)) {
		return null;
	}

	$timestamp = !empty($no_str) ? (int) $value : $value;
	return ci_format_datetime(
		$timestamp,
		config_item('date_format') . ' ' . config_item('time_format'),
		!empty($no_str)
	);
}

/**
 * Format dates without deprecated strftime tokens.
 */
function ci_format_datetime($value, $format, $is_timestamp = false)
{
	if ($value === null || $value === '') {
		return null;
	}
	$value = is_string($value) ? trim($value) : $value;
	if ($value === '' || $value === '0000-00-00' || $value === '0000-00-00 00:00:00') {
		return null;
	}

	try {
		$is_numeric_timestamp = is_int($value) || (is_string($value) && preg_match('/^-?\d{9,}$/', $value));
		if ($is_timestamp || $is_numeric_timestamp) {
			$dt = (new DateTime())->setTimestamp((int) $value);
		} else {
			$dt = new DateTime($value);
		}
	} catch (Throwable $e) {
		$timestamp = safe_strtotime($value);
		if ($timestamp === false) {
			return null;
		}
		$dt = (new DateTime())->setTimestamp($timestamp);
	}

	$php_format = ci_strftime_to_date_format($format);
	return $dt->format($php_format);
}

/**
 * Convert common strftime tokens to PHP date() format.
 */
function ci_strftime_to_date_format($format)
{
	$format = (string) $format;
	if (strpos($format, '%') === false) {
		return $format;
	}
	$map = array(
		'%%' => '%',
		'%Y' => 'Y',
		'%y' => 'y',
		'%m' => 'm',
		'%d' => 'd',
		'%e' => 'j',
		'%H' => 'H',
		'%I' => 'h',
		'%M' => 'i',
		'%S' => 's',
		'%p' => 'A',
		'%a' => 'D',
		'%A' => 'l',
		'%b' => 'M',
		'%B' => 'F',
		'%z' => 'O',
		'%Z' => 'T',
		'%R' => 'H:i',
		'%T' => 'H:i:s',
		'%F' => 'Y-m-d',
		'%D' => 'm/d/y',
	);
	return strtr($format, $map);
}


function custom_form_Fields($id, $edit_id = null, $col_sm = null)
{
	$CI = &get_instance();
	$all_field = $CI->db->where(array('form_id' => $id, 'status' => 'active'))->get('tbl_custom_field')->result();
	$form = $CI->db->where('form_id', $id)->get('tbl_form')->row();
	if (empty($form)) {
		return null;
	}
	$table = $form->tbl_name;
	$filed_id = $form->table_id;
	$html = null;
	$fields_html = '';
	$rendered_fields = 0;

	$render_custom_field = function ($field, $value, $name, $required_attr, $required_badge, $help_text) {
		$field_label = html_escape($field->field_label);
		$span_class = in_array($field->field_type, array('textarea', 'checkbox'), true) ? ' md:col-span-2' : '';
		$help_line = !empty($help_text) ? '<p class="mt-1 text-xs leading-5 text-muted-foreground">' . html_escape($help_text) . '</p>' : '';
		$header = '<div class="mb-3">
			<label class="block text-sm font-medium text-foreground">' . $field_label . ' ' . $required_badge . '</label>'
			. $help_line .
			'</div>';
		$wrapper_start = '<div class="rounded-2xl border border-border bg-background p-4' . $span_class . '">';
		$wrapper_end = '</div>';
		$value = is_array($value) ? $value : (string) $value;
		$base_input = 'w-full rounded-lg border border-border bg-input px-3 py-2.5 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-secondary/30';

		if ($field->field_type == 'text') {
			return $wrapper_start . $header . '<input type="text" name="' . html_escape($name) . '" class="' . $base_input . '" ' . $required_attr . ' value="' . html_escape($value) . '">' . $wrapper_end;
		}
		if ($field->field_type == 'email') {
			return $wrapper_start . $header . '<input type="email" name="' . html_escape($name) . '" class="' . $base_input . '" ' . $required_attr . ' value="' . html_escape($value) . '">' . $wrapper_end;
		}
		if ($field->field_type == 'textarea') {
			return $wrapper_start . $header . '<textarea name="' . html_escape($name) . '" rows="5" class="' . $base_input . '" ' . $required_attr . '>' . html_escape($value) . '</textarea>' . $wrapper_end;
		}
		if ($field->field_type == 'dropdown') {
			return $wrapper_start . $header . '<select name="' . html_escape($name) . '" class="select_box ' . $base_input . '" style="width:100%" ' . $required_attr . '>' . dropdownField($field->default_value, $value) . '</select>' . $wrapper_end;
		}
		if ($field->field_type == 'date') {
			$date_value = !empty($value) ? $value : date('Y-m-d');
			return $wrapper_start . $header . '<div class="relative">
				<input type="text" name="' . html_escape($name) . '" class="datepicker ' . $base_input . ' pr-10" ' . $required_attr . ' value="' . html_escape($date_value) . '">
				<span class="pointer-events-none absolute inset-y-0 right-3 flex items-center text-muted-foreground">
					<i data-lucide="calendar-days" class="h-3.5 w-3.5"></i>
				</span>
			</div>' . $wrapper_end;
		}
		if ($field->field_type == 'checkbox') {
			$options = json_decode($field->default_value, true);
			if (!is_array($options)) {
				$options = array_filter(array_map('trim', explode(',', (string) $field->default_value)));
			}
			$selected_values = json_decode((string) $value, true);
			if (!is_array($selected_values)) {
				$selected_values = array();
				if (!empty($value) && $value !== 'on') {
					$selected_values[] = (string) $value;
				}
			}
			$checkboxes = '';
			foreach ($options as $index => $option) {
				$option_label = html_escape($option);
				$option_id = $name . '_' . $index . '_' . substr(md5($name . $option), 0, 6);
				$is_checked = false;
				if ((string) $value === 'on') {
					$is_checked = true;
				} elseif (in_array((string) $option, array_map('strval', $selected_values), true)) {
					$is_checked = true;
				}
				$checkboxes .= '<label for="' . html_escape($option_id) . '" class="flex items-center gap-3 rounded-lg border border-border bg-card px-3 py-2.5 hover:bg-muted/40">
					<input id="' . html_escape($option_id) . '" type="checkbox" name="' . html_escape($name) . '[]" value="' . $option_label . '" class="h-3.5 w-3.5 rounded border-border text-primary focus:ring-primary/30"' . ($is_checked ? ' checked' : '') . ' ' . $required_attr . '>
					<span class="text-sm text-foreground">' . $option_label . '</span>
				</label>';
			}
			return $wrapper_start . $header . '<div class="grid gap-3 md:grid-cols-2">' . $checkboxes . '</div>' . $wrapper_end;
		}
		if ($field->field_type == 'numeric') {
			return $wrapper_start . $header . '<input type="number" name="' . html_escape($name) . '" class="' . $base_input . '" ' . $required_attr . ' value="' . html_escape($value) . '">' . $wrapper_end;
		}

		return '';
	};

	if (!empty($all_field)) {
		foreach ($all_field as $v_fileds) {
			$show_for_admin = !empty($v_fileds->visible_for_admin) && empty(client()) && !empty(admin());
			$show_for_client = !empty($v_fileds->visible_for_client) && !empty(client());
			$show_for_default_admin = empty(client()) && empty($v_fileds->visible_for_admin) && empty($v_fileds->visible_for_client);
			if (!$show_for_admin && !$show_for_client && !$show_for_default_admin) {
				continue;
			}

			$name = slug_it($v_fileds->field_label);
			$showValue = null;
			if (!empty($edit_id)) {
				$show_row = $CI->db->where($filed_id, $edit_id)->get($table)->row();
				if (!empty($show_row) && isset($show_row->$name)) {
					$showValue = $show_row->$name;
				}
			}
			if ($showValue !== null && $showValue !== '') {
				$value = $showValue;
			} else {
				$val = json_decode($v_fileds->default_value, true);
				$value = is_array($val) && array_key_exists(0, $val) ? $val[0] : '';
			}
			$required = $v_fileds->required == 'on' ? 'required' : '';
			$l_required = $v_fileds->required == 'on' ? '<span class="text-red-500">*</span>' : '';
			$help_text = !empty($v_fileds->help_text) ? $v_fileds->help_text : '';

			$fields_html .= $render_custom_field($v_fileds, $value, $name, $required, $l_required, $help_text);
			$rendered_fields++;
		}
	}

	if (!empty($fields_html)) {
		$html .= '<div class="md:col-span-2 rounded-2xl border border-border bg-card shadow-sm">
			<div class="flex flex-col gap-2 border-b border-border px-5 py-4 md:flex-row md:items-center md:justify-between">
				<div>
					<h3 class="text-base font-semibold text-foreground">' . lang('custom_field') . '</h3>
					<p class="text-sm text-muted-foreground">Additional fields for this record.</p>
				</div>
				<div class="text-xs font-medium text-muted-foreground">' . $rendered_fields . ' fields</div>
			</div>
			<div class="grid gap-4 p-5 md:grid-cols-2">'
			. $fields_html .
			'</div>
		</div>';
	}

	return $html;
}


function get_permission($colL, $col, $permission_user, $permissionL = null, $text = null)
{
	$CI = &get_instance();
	if (empty($text)) {
		$text = lang('permission');
	}

	$allow_permission = config_item('allow_custom_permission');

	$html = '';
	$html .= '<div class="form-group">
                <label class="col-lg-' . $colL . ' control-label">' . $text . '</label>
                <div class="col-lg-' . $col . '">
                <div class="checkbox c-radio needsclick"><label class="needsclick">
                <input id="" type="radio" name="permission" value="everyone"';
	if (!empty($permissionL) && $permissionL == 'all') {
		$html .= 'checked 2';
	} elseif (empty($allow_permission) && empty($permissionL)) {
		$html .= 'checked';
	}

	$html .= ' ><span class="circle"></span>' . lang('everyone') . '<i title="' . lang('permission_for_all') . '" class="circle-question-mark" data-toggle="tooltip data-placement="top"></i></label></div>';

	$html .= '<div class="checkbox c-radio needsclick"><label class="needsclick">
                <input id="" type="radio" name="permission" value="custom_permission"';
	if (!empty($permissionL) && $permissionL != 'all') {
		$html .= 'checked';
	} elseif (!empty($allow_permission) && empty($permissionL)) {
		$html .= 'checked';
	}
	$html .= ' ><span class="circle"></span>' . lang('custom_permission') . '<i title="' . lang('permission_for_customization') . '" class="circle-question-mark" data-toggle="tooltip data-placement="top"></i></label></div>';
	$html .= '</div></div>';
	if (!empty($permissionL) && $permissionL != 'all') {
		$user_id = json_decode($permissionL, true);
	}
	$html .= '<div class="form-group ';
	if (!empty($permissionL) && $permissionL != 'all') {
		$html .= 'show';
	} elseif (!empty($allow_permission) && empty($permissionL)) {
		$html .= 'show';
	}
	$html .= '" id="permission_user"><label for="field-1" class="col-sm-' . $colL . ' control-label">' . lang('select') . ' ' . lang('users') . '<span class="required">*</span></label>
<div class="col-lg-' . $col . '">';
	if (!empty($permission_user)) {
		foreach ($permission_user as $key => $v_user) {
			if ($v_user->role_id == 1) {
				$disable = true;
				$role = '<strong class="badge btn-danger">' . lang('admin') . '</strong>';
			} else {
				$disable = false;
				$role = '<strong class="badge btn-primary">' . lang('staff') . '</strong>';
			}
			$html .= '<div class="checkbox c-checkbox needsclick"><label class="needsclick">
                <input  class="needsclick assigned_to_modal" type="checkbox" name="assigned_to[]" value="' . $v_user->user_id . '"';
			if (!empty($permissionL) && $permissionL != 'all') {
				if (array_key_exists($v_user->user_id, $user_id)) {
					$html .= 'checked';
				}
			} elseif (!empty($allow_permission) && empty($permissionL)) {
				if (my_id() == $v_user->user_id) {
					$html .= 'checked';
				}
			}
			$html .= ' ><span class="check"></span>' . $v_user->username . ' ' . $role . ' </label></div>
            <div class="action_1 p ';
			if (!empty($permissionL) && $permissionL != 'all') {
				if (array_key_exists($v_user->user_id, $user_id)) {
					$html .= 'show';
				}
			} elseif (!empty($allow_permission) && empty($permissionL)) {
				if (my_id() == $v_user->user_id) {
					$html .= 'show';
				}
			}
			$html .= ' " id="action_1' . $v_user->user_id . '"><label class="checkbox-inline c-checkbox"><input id="' . $v_user->user_id . '" checked type="checkbox" name="action_1' . $v_user->user_id . '[]" disabled value="view"> <span class="check"></span>' . lang('can') . ' ' . lang('view') . '</label>';
			$html .= '<label class="checkbox-inline c-checkbox"><input ' . (!empty($disable) ? 'disabled' . ' ' . 'checked' : ' ') . ' id="' . $v_user->user_id . '"  type="checkbox" name="action_' . $v_user->user_id . '[]" value="edit"';
			if (!empty($permissionL) && $permissionL != 'all') {
				foreach ($user_id as $uid => $v_permission) {
					if ($uid == $v_user->user_id) {
						if (in_array('edit', $v_permission)) {
							$html .= 'checked';
						};
					}
				}
			} elseif (!empty($allow_permission) && empty($permissionL)) {
				if (my_id() == $v_user->user_id) {
					$html .= 'checked';
				}
			}
			$html .= '> <span class="check"></span>' . lang('can') . ' ' . lang('edit') . '</label>';
			$html .= '<label class="checkbox-inline c-checkbox"><input ' . (!empty($disable) ? 'disabled' . ' ' . 'checked' : ' ') . ' id="' . $v_user->user_id . '"  type="checkbox" name="action_' . $v_user->user_id . '[]" value="delete"';
			if (!empty($permissionL) && $permissionL != 'all') {
				foreach ($user_id as $uid => $v_permission) {
					if ($uid == $v_user->user_id) {
						if (in_array('delete', $v_permission)) {
							$html .= 'checked';
						};
					}
				}
			} elseif (!empty($allow_permission) && empty($permissionL)) {
				if (my_id() == $v_user->user_id) {
					$html .= 'checked';
				}
			}
			$html .= '> <span class="check"></span>' . lang('can') . ' ' . lang('delete') . '</label><input id="' . $v_user->user_id . '" type="hidden" name="action_' . $v_user->user_id . '[]" value="view"> </div>';
		}
	}
	$html .= '</div></div>';
	return $html;
}

/**
 * Modern permission selector with radio (everyone/custom) + user table.
 */
function form_permission_modern($permission_user, $permissionL = null, $label = null, $no_label = false)
{
	if (empty($label)) {
		$label = lang('permission');
	}
	$uid = uniqid('perm_');
	// decode stored permissions
	$selected = array();
	if (!empty($permissionL) && $permissionL != 'all') {
		$selected = json_decode($permissionL, true) ?: array();
	}

	// helper to render selected cards (for edit)
	$rows_html = '';
	if (!empty($selected)) {
		foreach ($selected as $user_id => $perms) {
			$user = get_staff_details($user_id);
			if (!$user) {
				continue;
			}
			$avatar = !empty($user->avatar) ? $user->avatar : '';
			$initial = strtoupper(substr($user->fullname, 0, 1));
				$rows_html .= '
			<div class="flex min-w-0 flex-col gap-3 rounded-2xl border border-border bg-white p-3 shadow-sm transition-all hover:shadow-md dark:bg-slate-800 sm:p-4"
			     data-permission-card="' . $user_id . '">
				<div class="flex min-w-0 items-center gap-3">
					<div class="flex-shrink-0 h-10 w-10 rounded-full bg-slate-100 border border-slate-200 flex items-center justify-center text-sm font-bold text-slate-600 overflow-hidden">
						' . (!empty($avatar) ? '<img src="' . base_url($avatar) . '" alt="' . html_escape($user->fullname) . '" class="h-10 w-10 object-cover">' : $initial) . '
					</div>
					<div class="min-w-0 flex-1">
						<div class="truncate text-sm font-semibold text-slate-900 dark:text-slate-100">' . html_escape($user->fullname) . '</div>
						<div class="truncate text-xs text-slate-500 dark:text-slate-400">
							@' . html_escape($user->username) . ' - ' . (!empty($user->designations) ? html_escape($user->designations) : lang('staff')) . '
						</div>
					</div>
					<input type="hidden" name="assigned_to[]" value="' . $user_id . '">
				</div>
				<div class="flex min-w-0 flex-wrap items-center gap-2">
					<label class="cursor-pointer group">
						<input type="checkbox" name="perm_view_' . $user_id . '" value="1" checked class="peer sr-only">
						<div class="permission-pill-content flex items-center gap-1.5 px-2 py-1 rounded-lg border border-border bg-white dark:bg-slate-800 text-xs font-medium text-slate-500 dark:text-slate-400 hover:bg-gray-50 transition-all peer-checked:bg-blue-50 peer-checked:border-blue-200 peer-checked:text-blue-700">
							<i data-lucide="eye" class="w-3.5 h-3.5"></i>
							<span>' . lang('view') . '</span>
						</div>
					</label>
					<label class="cursor-pointer group">
						<input type="checkbox" name="perm_edit_' . $user_id . '" value="1" ' . (!empty($perms) && in_array('edit', $perms) ? 'checked' : '') . ' class="peer sr-only">
						<div class="permission-pill-content flex items-center gap-1.5 px-2 py-1 rounded-lg border border-border bg-white dark:bg-slate-800 text-xs font-medium text-slate-500 dark:text-slate-400 hover:bg-gray-50 transition-all peer-checked:bg-blue-50 peer-checked:border-blue-200 peer-checked:text-blue-700">
							<i data-lucide="edit-2" class="w-3.5 h-3.5"></i>
							<span>' . lang('edit') . '</span>
						</div>
					</label>
					<label class="cursor-pointer group">
						<input type="checkbox" name="perm_delete_' . $user_id . '" value="1" ' . (!empty($perms) && in_array('delete', $perms) ? 'checked' : '') . ' class="peer sr-only">
						<div class="permission-pill-content flex items-center gap-1.5 px-2 py-1 rounded-lg border border-border bg-white dark:bg-slate-800 text-xs font-medium text-slate-500 dark:text-slate-400 hover:bg-gray-50 transition-all peer-checked:bg-red-50 peer-checked:border-red-200 peer-checked:text-red-700">
							<i data-lucide="trash" class="w-3.5 h-3.5"></i>
							<span>' . lang('delete') . '</span>
						</div>
					</label>
					<div class="mx-1 hidden h-4 w-px bg-gray-200 dark:bg-gray-600 sm:block"></div>
					<button type="button" onclick="removeUserRow(this)" class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-slate-400 transition-colors hover:bg-red-50 hover:text-red-600" title="' . lang('remove') . '">
						<i data-lucide="x" class="w-4 h-4"></i>
					</button>
				</div>
			</div>';
		}
	}

	// determine default radio
	// $everyone_checked = null;
	$everyone_checked = (empty($permissionL) || $permissionL == 'all');
	static $permission_fallback_printed = false;

	ob_start();
?>
	<?php if (!$permission_fallback_printed) :
		$permission_fallback_printed = true; ?>
		<script>
			if (typeof window.togglePermissions !== 'function') {
				window.togglePermissions = function(radio) {
					if (!radio) return;
					var root = radio.closest('[data-permission-root]') || document;
					root.querySelectorAll('input[name="permission"]').forEach(function(item) {
						var label = item.parentElement;
						if (!label) return;
						var check = label.querySelector('.check-icon');
						var uncheck = label.querySelector('.uncheck-icon');
						if (item.checked) {
							label.classList.add('border-primary-700', 'ring-1', 'ring-primary-700', 'bg-primary-50', 'bg-opacity-10');
							if (check) check.classList.remove('hidden');
							if (uncheck) uncheck.classList.add('hidden');
						} else {
							label.classList.remove('border-primary-700', 'ring-1', 'ring-primary-700', 'bg-primary-50', 'bg-opacity-10');
							if (check) check.classList.add('hidden');
							if (uncheck) uncheck.classList.remove('hidden');
						}
					});
					var customArea = root.querySelector('[data-permission-custom]') || root.querySelector('#custom_permissions_area');
					if (customArea) {
						customArea.classList.toggle('hidden', radio.value !== 'custom_permission');
					}
				};
			}
		</script>
	<?php endif; ?>

	<div class="<?= ($no_label ? '' : 'rounded-2xl border border-border bg-card p-4') ?>" data-permission-root>
		<h4 class="flex items-center gap-2 text-sm font-semibold text-foreground mb-4 <?= ($no_label ? 'hidden' : '') ?>">
			<i data-lucide="shield" class="h-3.5 w-3.5"></i> <?= $label ?>
		</h4>
		<!-- Permission Options -->
		<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
			<!-- Option 1: Everyone -->
			<label class="relative flex cursor-pointer rounded-xl p-4 shadow-sm border border-border focus:outline-none hover:border-primary-400 hover:bg-primary-50/10 transition-all bg-input">
				<input type="radio" name="permission" value="everyone" class="sr-only" <?= $everyone_checked ? 'checked' : ''; ?> onchange="togglePermissions(this)">
				<span class="flex flex-1">
					<span class="flex flex-col">
						<span class="block text-sm font-medium mb-1 text-slate-900 dark:text-slate-100"><?= lang('everyone'); ?></span>
						<span class="block text-xs text-slate-500 dark:text-slate-400"><?= lang('visible_to_all_admins_and_staff_with_client_access'); ?></span>
					</span>
				</span>
				<div class="check-icon hidden text-primary-600">
					<i data-lucide="check-circle-2" class="w-5 h-5"></i>
				</div>
				<div class="uncheck-icon block text-gray-300">
					<i data-lucide="circle" class="w-5 h-5"></i>
				</div>
			</label>

			<!-- Option 2: Custom -->
			<label class="relative flex cursor-pointer rounded-xl p-4 shadow-sm focus:outline-none border border-border hover:border-primary-400 hover:bg-primary-50/10 transition-all bg-input">
				<input type="radio" name="permission" value="custom_permission" class="sr-only" onchange="togglePermissions(this)" <?= !$everyone_checked ? 'checked' : ''; ?>>
				<span class="flex flex-1">
					<span class="flex flex-col">
						<span class="block text-sm font-medium mb-1 text-slate-900 dark:text-slate-100"><?= lang('restricted_access'); ?></span>
						<span class="block text-xs text-slate-500 dark:text-slate-400"><?= lang('only_specific_users_can_view_or_manage_this'); ?></span>
					</span>
				</span>
				<div class="check-icon hidden text-primary-600">
					<i data-lucide="check-circle-2" class="w-5 h-5"></i>
				</div>
				<div class="uncheck-icon block text-gray-300">
					<i data-lucide="circle" class="w-5 h-5"></i>
				</div>
			</label>
		</div>
		<!-- Custom Permissions Area -->
		<div id="custom_permissions_area" data-permission-custom class="hidden animate-fade-in space-y-4">
			<!-- Modern User Selector -->
				<div class="relative flex flex-col gap-2 rounded-2xl border border-border bg-card p-2.5 shadow-sm transition-all focus-within:border-primary-500 focus-within:ring-2 focus-within:ring-primary-500 sm:flex-row sm:items-stretch sm:gap-0">
					<div class="flex w-full items-center justify-center rounded-xl border border-border bg-gray-200 px-4 py-2 text-sm text-muted-foreground dark:bg-gray-700 sm:w-auto sm:rounded-r-none">
						<i data-lucide="user-plus" class="w-5 h-5"></i>
					</div>
					<select id="user_select" data-permission-user-select class="w-full rounded-xl border border-border bg-input px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-secondary/30 dark:border-slate-600 sm:rounded-l-none sm:rounded-r-xl tw-selectpicker" data-placeholder="Select a user to grant access..."
						onchange="addUserRow(this)">
					<?php foreach ($permission_user as $u) :
						$fullname = !empty($u->fullname) ? $u->fullname : (!empty($u->username) ? $u->username : lang('unknown'));
						$designation = !empty($u->designations) ? $u->designations : '';
						$username = !empty($u->username) ? $u->username : '';
						$role_id = isset($u->role_id) ? $u->role_id : '';
						$avatar = !empty($u->avatar) ? $u->avatar : '';
						$label = $fullname . (!empty($designation) ? ' (' . $designation . ')' : '');
					?>
						<option value="<?= $u->user_id; ?>" data-username="<?= html_escape($username); ?>" data-role="<?= html_escape($role_id); ?>" data-avatar="<?= html_escape($avatar); ?>" data-name="<?= html_escape($fullname); ?>" data-designation="<?= html_escape($designation); ?>"><?= html_escape($label); ?></option>
					<?php endforeach; ?>
				</select>

			</div>

			<!-- Modern Permissions List (Card-based Table) -->
			<div id="permission_list_body" data-permission-list class="mt-4 space-y-2">
				<?= $rows_html ?>
				<!-- Empty State -->
					<div id="empty_state_row" data-permission-empty class="rounded-2xl border-2 border-dashed border-border bg-input py-10 text-center" style="<?= !empty($rows_html) ? 'display:none;' : '' ?>">
						<div class="mb-3 inline-flex h-12 w-12 items-center justify-center rounded-full border border-border bg-white shadow-sm">
						<i data-lucide="users" class="w-5 h-5 text-gray-400"></i>
					</div>
					<h3 class="text-sm font-medium text-slate-900 dark:text-slate-100">No users assigned</h3>
					<p class="text-xs text-slate-500 dark:text-slate-400 mt-1">Select a user above to grant custom permissions.</p>
				</div>
			</div>
		</div>
	</div>
	<script>
		(function () {
			if (window.__permissionModernInit) {
				return;
			}
			window.__permissionModernInit = true;

			function permissionRefreshSelectpicker(select) {
				if (!select) return;
				if (window.refreshSelectpicker) {
					window.refreshSelectpicker(select);
				} else if (window.jQuery && window.jQuery.fn && window.jQuery.fn.selectpicker) {
					window.jQuery(select).selectpicker("refresh");
				}
			}

			window.togglePermissions = function (radio) {
				if (!radio) return;
				var root = radio.closest("[data-permission-root]") || document;
				root.querySelectorAll('input[name="permission"]').forEach(function (r) {
					var label = r.parentElement;
					var check = label ? label.querySelector(".check-icon") : null;
					var uncheck = label ? label.querySelector(".uncheck-icon") : null;
					if (r.checked) {
						label.classList.add("border-primary-700", "ring-1", "ring-primary-700", "bg-primary-50", "bg-opacity-10");
						if (check) check.classList.remove("hidden");
						if (uncheck) uncheck.classList.add("hidden");
					} else {
						label.classList.remove("border-primary-700", "ring-1", "ring-primary-700", "bg-primary-50", "bg-opacity-10");
						if (check) check.classList.add("hidden");
						if (uncheck) uncheck.classList.remove("hidden");
					}
				});
				var customArea =
					root.querySelector("[data-permission-custom]") ||
					root.querySelector("#custom_permissions_area");
				if (customArea) {
					if (radio.value === "custom_permission") {
						customArea.classList.remove("hidden");
					} else {
						customArea.classList.add("hidden");
					}
				}
			};

			window.addUserRow = function (btn) {
				var root = btn ? btn.closest("[data-permission-root]") : document;
				var select =
					root.querySelector("[data-permission-user-select]") ||
					root.querySelector("#user_select");
				var container =
					root.querySelector("[data-permission-list]") ||
					root.querySelector("#permission_list_body");
				var emptyState =
					root.querySelector("[data-permission-empty]") ||
					root.querySelector("#empty_state_row");
				if (!select || !container) return;

				var userId = select.value;
				if (!userId) return;

				var option = select.options[select.selectedIndex];
				var userFullName = option && option.dataset && option.dataset.name ? option.dataset.name : (option ? option.text : "");
				var avatar = option && option.dataset ? option.dataset.avatar || "" : "";
				var userName = option && option.dataset ? option.dataset.username || "" : "";
				var userDesignation = option && option.dataset ? option.dataset.designation || "" : "";

				if (emptyState) emptyState.style.display = "none";
				if (root.querySelector('[data-permission-card="' + userId + '"]')) {
					if (window.toaster && typeof window.toaster.info === "function") {
						window.toaster.info("User already added.");
					} else {
						alert("User already added.");
					}
					return;
				}

				var rowDiv = document.createElement("div");
				rowDiv.setAttribute("data-permission-card", userId);
				rowDiv.className = "flex min-w-0 flex-col gap-3 rounded-2xl border border-border bg-white p-3 shadow-sm transition-all hover:shadow-md dark:bg-slate-800 sm:p-4";
				rowDiv.innerHTML =
					'<div class="flex min-w-0 items-center gap-3">' +
						'<div class="flex-shrink-0 h-10 w-10 rounded-full bg-gray-100 flex items-center justify-center text-sm font-bold text-gray-600 border border-gray-200">' +
							(avatar ? '<img src="' + base_url + avatar + '" alt="' + userFullName + '" class="h-10 w-10 rounded-full object-cover">' : String(userFullName || '?').charAt(0).toUpperCase()) +
						'</div>' +
						'<div class="min-w-0 flex-1">' +
							'<div class="truncate text-sm font-semibold text-slate-900 dark:text-slate-100">' + userFullName + '</div>' +
							'<div class="truncate text-xs text-slate-500 dark:text-slate-400"><a target="_blank" href="' + base_url + 'admin/users/user_details/' + userId + '" class="text-blue-600 dark:text-blue-400">@' + userName + '</a> - ' + userDesignation + '</div>' +
						'</div>' +
						'<input type="hidden" name="assigned_to[]" value="' + userId + '">' +
					'</div>' +
					'<div class="flex min-w-0 flex-wrap items-center gap-2">' +
						'<label class="cursor-pointer group">' +
							'<input type="checkbox" name="perm_view_' + userId + '" value="1" checked class="peer sr-only">' +
							'<div class="permission-pill-content flex items-center gap-1.5 px-2 py-1 rounded-lg border border-border bg-white dark:bg-slate-800 text-xs font-medium text-slate-500 dark:text-slate-400 hover:bg-gray-50 transition-all peer-checked:bg-blue-50 peer-checked:border-blue-200 peer-checked:text-blue-700">' +
								'<i data-lucide="eye" class="w-3.5 h-3.5"></i><span>' + <?= json_encode(lang('view')) ?> + '</span>' +
							'</div>' +
						'</label>' +
						'<label class="cursor-pointer group">' +
							'<input type="checkbox" name="perm_edit_' + userId + '" value="1" checked class="peer sr-only">' +
							'<div class="permission-pill-content flex items-center gap-1.5 px-2 py-1 rounded-lg border border-border bg-white dark:bg-slate-800 text-xs font-medium text-slate-500 dark:text-slate-400 hover:bg-gray-50 transition-all peer-checked:bg-blue-50 peer-checked:border-blue-200 peer-checked:text-blue-700">' +
								'<i data-lucide="edit-2" class="w-3.5 h-3.5"></i><span>' + <?= json_encode(lang('edit')) ?> + '</span>' +
							'</div>' +
						'</label>' +
						'<label class="cursor-pointer group">' +
							'<input type="checkbox" name="perm_delete_' + userId + '" value="1" checked class="peer sr-only">' +
							'<div class="permission-pill-content flex items-center gap-1.5 px-2 py-1 rounded-lg border border-border bg-white dark:bg-slate-800 text-xs font-medium text-slate-500 dark:text-slate-400 hover:bg-gray-50 transition-all peer-checked:bg-red-50 peer-checked:border-red-200 peer-checked:text-red-700">' +
								'<i data-lucide="trash" class="w-3.5 h-3.5"></i><span>' + <?= json_encode(lang('delete')) ?> + '</span>' +
							'</div>' +
						'</label>' +
						'<div class="mx-1 hidden h-4 w-px bg-gray-200 dark:bg-gray-600 sm:block"></div>' +
						'<button type="button" onclick="removeUserRow(this)" class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-slate-400 transition-colors hover:bg-red-50 hover:text-red-600" title="' + <?= json_encode(lang('remove')) ?> + '">' +
							'<i data-lucide="x" class="w-4 h-4"></i>' +
						'</button>' +
					'</div>';
				container.appendChild(rowDiv);
				select.value = "";
				permissionRefreshSelectpicker(select);
				if (window.lucide && typeof window.lucide.createIcons === "function") {
					window.lucide.createIcons();
				}
			};

			window.removeUserRow = function (btn) {
				var row = btn ? btn.closest("[data-permission-card]") : null;
				if (!row) return;
				var root = row.closest("[data-permission-root]") || document;
				var select =
					root.querySelector("[data-permission-user-select]") ||
					root.querySelector("#user_select");
				var container =
					root.querySelector("[data-permission-list]") ||
					root.querySelector("#permission_list_body");
				var emptyState =
					root.querySelector("[data-permission-empty]") ||
					root.querySelector("#empty_state_row");

				row.style.opacity = "0";
				row.style.transform = "scale(0.95)";
				setTimeout(function () {
					row.remove();
					if (select) {
						var option = select.querySelector('option[value="' + row.dataset.permissionCard + '"]');
						if (option) option.disabled = false;
						permissionRefreshSelectpicker(select);
					}
					if (container && !container.querySelector("[data-permission-card]") && emptyState) {
						emptyState.style.display = "block";
					}
					if (window.lucide && typeof window.lucide.createIcons === "function") {
						window.lucide.createIcons();
					}
				}, 200);
			};

			document.querySelectorAll("[data-permission-root]").forEach(function (root) {
				var checkedRadio = root.querySelector('input[name="permission"]:checked');
				if (checkedRadio) {
					window.togglePermissions(checkedRadio);
				}
			});
		})();
	</script>
<?php
	return ob_get_clean();
}


function get_permission_modal($colL, $col, $permission_user, $permissionL = null, $text = null)
{
	if (empty($text)) {
		$text = lang('permission');
	}
	$allow_permission = config_item('allow_custom_permission');

	$html = '';
	$html .= '<div class="form-group">
                <label class="col-lg-' . $colL . ' control-label">' . $text . '</label>
                <div class="col-lg-' . $col . '">
                <div class="checkbox c-radio needsclick"><label class="needsclick">
                <input class="permission_user_modal" type="radio" name="permission" value="everyone"';
	if (!empty($permissionL) && $permissionL == 'all') {
		$html .= 'checked';
	} elseif (empty($allow_permission) && empty($permissionL)) {
		$html .= 'checked';
	}

	$html .= ' ><span class="circle"></span>' . lang('everyone') . '<i title="' . lang('permission_for_all') . '" class="circle-question-mark" data-toggle="tooltip data-placement="top"></i></label></div>';

	$html .= '<div class="checkbox c-radio needsclick"><label class="needsclick">
                <input class="permission_user_modal" type="radio" name="permission" value="custom_permission"';
	if (!empty($permissionL) && $permissionL != 'all') {
		$html .= 'checked';
	} elseif (!empty($allow_permission) && empty($permissionL)) {
		$html .= 'checked';
	}
	$html .= ' ><span class="circle"></span>' . lang('custom_permission') . '<i title="' . lang('permission_for_customization') . '" class="circle-question-mark" data-toggle="tooltip data-placement="top"></i></label></div>';
	$html .= '</div></div>';
	if (!empty($permissionL) && $permissionL != 'all') {
		$user_id = json_decode($permissionL, true);
	}
	$html .= '<div class="form-group ';
	if (!empty($permissionL) && $permissionL != 'all') {
		$html .= 'show';
	} elseif (!empty($allow_permission) && empty($permissionL)) {
		$html .= 'show';
	}
	$html .= '" id="permission_user_modal"><label for="field-1" class="col-sm-' . $colL . ' control-label">' . lang('select') . ' ' . lang('users') . '<span class="required">*</span></label>
<div class="col-lg-' . $col . '">';
	if (!empty($permission_user)) {
		foreach ($permission_user as $key => $v_user) {
			if ($v_user->role_id == 1) {
				$disable = true;
				$role = '<strong class="badge btn-danger">' . lang('admin') . '</strong>';
			} else {
				$disable = false;
				$role = '<strong class="badge btn-primary">' . lang('staff') . '</strong>';
			}
			$html .= '<div class="checkbox c-checkbox needsclick"><label class="needsclick">
                <input class="needsclick assigned_to_modal" type="checkbox" name="assigned_to[]" value="' . $v_user->user_id . '"';
			if (!empty($permissionL) && $permissionL != 'all') {
				if (array_key_exists($v_user->user_id, $user_id)) {
					$html .= 'checked';
				}
			} elseif (!empty($allow_permission) && empty($permissionL)) {
				if (my_id() == $v_user->user_id) {
					$html .= 'checked';
				}
			}
			$html .= ' ><span class="check"></span>' . $v_user->username . ' ' . $role . ' </label></div>
            <div class="action_modal p ';
			if (!empty($permissionL) && $permissionL != 'all') {
				if (array_key_exists($v_user->user_id, $user_id)) {
					$html .= 'show';
				}
			} elseif (!empty($allow_permission) && empty($permissionL)) {
				if (my_id() == $v_user->user_id) {
					$html .= 'show';
				}
			}
			$html .= ' " id="action_u_modal_' . $v_user->user_id . '"><label class="checkbox-inline c-checkbox"><input id="' . $v_user->user_id . '" checked type="checkbox" name="action_' . $v_user->user_id . '[]" disabled value="view"> <span class="check"></span>' . lang('can') . ' ' . lang('view') . '</label>';
			$html .= '<label class="checkbox-inline c-checkbox"><input ' . (!empty($disable) ? 'disabled' . ' ' . 'checked' : ' ') . ' id="' . $v_user->user_id . '"  type="checkbox" name="action_' . $v_user->user_id . '[]" value="edit"';
			if (!empty($permissionL) && $permissionL != 'all') {
				foreach ($user_id as $uid => $v_permission) {
					if ($uid == $v_user->user_id) {
						if (in_array('edit', $v_permission)) {
							$html .= 'checked';
						};
					}
				}
			} elseif (!empty($allow_permission) && empty($permissionL)) {
				if (my_id() == $v_user->user_id) {
					$html .= 'checked';
				}
			}
			$html .= '> <span class="check"></span>' . lang('can') . ' ' . lang('edit') . '</label>';
			$html .= '<label class="checkbox-inline c-checkbox"><input ' . (!empty($disable) ? 'disabled' . ' ' . 'checked' : ' ') . ' id="' . $v_user->user_id . '"  type="checkbox" name="action_' . $v_user->user_id . '[]" value="delete"';
			if (!empty($permissionL) && $permissionL != 'all') {
				foreach ($user_id as $uid => $v_permission) {
					if ($uid == $v_user->user_id) {
						if (in_array('delete', $v_permission)) {
							$html .= 'checked';
						};
					}
				}
			} elseif (!empty($allow_permission) && empty($permissionL)) {
				if (my_id() == $v_user->user_id) {
					$html .= 'checked';
				}
			}
			$html .= '> <span class="check"></span>' . lang('can') . ' ' . lang('delete') . '</label><input id="' . $v_user->user_id . '" type="hidden" name="action_' . $v_user->user_id . '[]" value="view"> </div>';
		}
	}
	$html .= '</div></div>';

	return $html;
}

function dropdownField($value, $editValue = null)
{
	$html = null;
	$options = json_decode((string) $value);
	if (!is_array($options) && !($options instanceof Traversable)) {
		return $html;
	}
	foreach ($options as $optionValue) {
		$html .= '<option value="' . $optionValue . '" ' . (!empty($editValue) && $editValue == $optionValue ? "selected" : null) . '>' . $optionValue . '</option>';
	}
	return $html;
}

function save_custom_field($id, $edit_id = null, $post_data = [])
{
	$CI = &get_instance();
	$CI->load->model('admin_model');

	if (!empty($post_data)) {
		$psot = $post_data;
	} else {
		$psot = $CI->input->post();
	}

	$all_field = $CI->db->where(array('form_id' => $id, 'status' => 'active'))->get('tbl_custom_field')->result();
	$form = $CI->db->where('form_id', $id)->get('tbl_form')->row();
	if (empty($form) || empty($form->tbl_name) || empty($form->table_id)) {
		return;
	}
	$table = $form->tbl_name;
	$table_id = $form->table_id;
	$custom = array();
	if (!empty($all_field)) {
		foreach ($all_field as $v_fileds) {
			$name = slug_it($v_fileds->field_label);
			if (!$CI->db->field_exists($name, $table)) {
				continue;
			}
			$post = array_key_exists($name, $psot) ? $psot[$name] : null;
			if (is_array($post)) {
				$post = json_encode($post);
			}

			if (!empty($v_fileds->visible_for_admin) && empty(client())) {
				if (!empty(admin())) {
					$custom[$name] = $post;
				}
			} else if (!empty($v_fileds->visible_for_client) && !empty(client())) {
				if (!empty(client())) {
					$custom[$name] = $post;
				}
			} else if (empty(client())) {
				$custom[$name] = $post;
			}
		}

		$CI->admin_model->_table_name = $table; //table name
		$CI->admin_model->_primary_key = $table_id;
		if (!empty($custom)) {
			$rr = $CI->admin_model->save($custom, $edit_id);
		}
	}
}

function custom_form_label($id, $show_id)
{
	$CI = &get_instance();
	$CI->load->model('admin_model');
	$all_field = $CI->db->where(array('form_id' => $id, 'status' => 'active'))->get('tbl_custom_field')->result();
	$form = $CI->db->where('form_id', $id)->get('tbl_form')->row();
	$table = $form->tbl_name;
	$table_id = $form->table_id;

	$showValue = array();
	if (!empty($all_field)) {
		foreach ($all_field as $v_fileds) {
			if (!empty($v_fileds->visible_for_admin) && empty(client())) {
				if (!empty(admin())) {
					$name = slug_it($v_fileds->field_label);
					$showValue[$v_fileds->field_label] = $CI->db->where($table_id, $show_id)->get($table)->row($name);
				}
			} else if (!empty($v_fileds->visible_for_client) && !empty(client())) {
				if (!empty(client())) {
					$name = slug_it($v_fileds->field_label);
					$showValue[$v_fileds->field_label] = $CI->db->where($table_id, $show_id)->get($table)->row($name);
				}
			} else if (empty(client())) {
				$name = slug_it($v_fileds->field_label);
				$showValue[$v_fileds->field_label] = $CI->db->where($table_id, $show_id)->get($table)->row($name);
			}
		}
	}
	return $showValue;
}

function custom_form_table($id, $show_id)
{
	$CI = &get_instance();
	$CI->load->model('admin_model');
	$all_field = $CI->db->where(array('form_id' => $id, 'status' => 'active'))->get('tbl_custom_field')->result();
	$form = $CI->db->where('form_id', $id)->get('tbl_form')->row();
	$table = $form->tbl_name;
	$table_id = $form->table_id;

	$showValue = array();
	if (!empty($all_field)) {
		foreach ($all_field as $v_fileds) {
			if (!empty($v_fileds->visible_for_admin) && empty(client())) {
				if (!empty(admin())) {
					if ($v_fileds->show_on_table == 'on') {
						$name = slug_it($v_fileds->field_label);
						$showValue[$v_fileds->field_label] = $CI->db->where($table_id, $show_id)->get($table)->row($name);
					}
				}
			} else if (!empty($v_fileds->visible_for_client) && !empty(client())) {
				if (!empty(client())) {
					if ($v_fileds->show_on_table == 'on') {
						$name = slug_it($v_fileds->field_label);
						$showValue[$v_fileds->field_label] = $CI->db->where($table_id, $show_id)->get($table)->row($name);
					}
				}
			} else if (empty(client())) {
				if ($v_fileds->show_on_table == 'on') {
					$name = slug_it($v_fileds->field_label);
					$showValue[$v_fileds->field_label] = $CI->db->where($table_id, $show_id)->get($table)->row($name);
				}
			}
		}
	}
	return $showValue;
}

function custom_form_cron($id, $show_id)
{
	$CI = &get_instance();
	$CI->load->model('admin_model');
	$all_field = $CI->db->where(array('form_id' => $id, 'status' => 'active'))->get('tbl_custom_field')->result();
	$form = $CI->db->where('form_id', $id)->get('tbl_form')->row();
	$table = $form->tbl_name;
	$table_id = $form->table_id;

	$showValue = array();
	if (!empty($all_field)) {
		foreach ($all_field as $v_fileds) {
			if (!empty($v_fileds->visible_for_admin) && empty(client())) {
				if (!empty(admin())) {
					$name = slug_it($v_fileds->field_label);
					$showValue[$name] = $CI->db->where($table_id, $show_id)->get($table)->row($name);
				}
			} else if (!empty($v_fileds->visible_for_client) && !empty(client())) {
				if (!empty(client())) {
					$name = slug_it($v_fileds->field_label);
					$showValue[$name] = $CI->db->where($table_id, $show_id)->get($table)->row($name);
				}
			} else if (empty(client())) {
				$name = slug_it($v_fileds->field_label);
				$showValue[$name] = $CI->db->where($table_id, $show_id)->get($table)->row($name);
			}
		}
	}
	return $showValue;
}

function form_custom_fields($id)
{
	$cfields = array();

	$CI = &get_instance();
	$CI->load->model('admin_model');
	$all_field = $CI->db->where(array('form_id' => $id, 'status' => 'active'))->get('tbl_custom_field')->result();

	$form = $CI->db->where('form_id', $id)->get('tbl_form')->row();
	$table = $form->tbl_name;
	$table_id = $form->table_id;

	foreach ($all_field as $f) {

		$_field_object = new stdClass();
		$type = $f->field_type;

		$subtype = '';
		$className = 'form-control';

		if ($f->field_type == 'date') {
			$type = 'date';
		} elseif ($f->field_type == 'checkbox') {
			$type = 'checkbox';
			$className = '';
			//            if ($f['display_inline'] == 1) {
			$className .= 'form-inline-checkbox';
			//            }
		} elseif ($f->field_type == 'input') {
			$type = 'text';
		} elseif ($f->field_type == 'email') {
			$type = 'text';
		} elseif ($f->field_type == 'numeric') {
			$type = 'number';
		} elseif ($f->field_type == 'dropdown') {
			$type = 'select';
		}
		$field_array = [
			'subtype' => $subtype,
			'type' => $type,
			'label' => $f->field_label,
			'className' => $className,
			'name' => slug_it($f->field_label),
		];

		if ($f->required == 'on') {
			$field_array['required'] = true;
		}
		if (!empty($f->help_text)) {
			$field_array['description'] = $f->help_text;
		}
		if (!empty($f->default_value)) {
			$default_value = json_decode($f->default_value);
			$field_array['value'] = $default_value[0];
		}

		if ($f->field_type == 'dropdown') {
			$field_array['values'] = [];
			$options = json_decode((string) $f->default_value);
			if (!is_array($options) && !($options instanceof Traversable)) {
				$options = [];
			}
			// leave first field blank
			if ($f->field_type == 'dropdown') {
				array_push($field_array['values'], [
					'label' => '',
					'value' => '',
				]);


				foreach ($options as $option) {
					$option = trim($option);
					if ($option != '') {
						array_push($field_array['values'], [
							'label' => $option,
							'value' => $option,
						]);
					}
				}
			}
		}

		$_field_object->label = $f->field_label;
		$_field_object->name = slug_it($f->field_label);
		$_field_object->fields = [];
		$_field_object->fields[] = $field_array;
		$cfields[] = $_field_object;
	}

	return $cfields;
}

function url_encode($data)
{
	return base64_encode(serialize($data));
}

function url_decode($data)
{
	return unserialize(base64_decode($data));
}

function encrypt($data)
{
	return get_instance()->encryption->encrypt($data);
}

function decrypt($data)
{
	return get_instance()->encryption->decrypt($data);
}

// Generic cache helpers (uses CI cache driver if loaded)
function helper_cache_get($key)
{
	$CI = &get_instance();
	return isset($CI->cache) ? $CI->cache->get($key) : false;
}

function helper_cache_save($key, $value, $ttl = 300)
{
	$CI = &get_instance();
	if (isset($CI->cache)) {
		$CI->cache->save($key, $value, $ttl);
	}
}

/**
 * Internal cache helper for role checks.
 */
function _role_cache_get($key)
{
	$CI = &get_instance();
	return isset($CI->cache) ? $CI->cache->get($key) : false;
}

function _role_cache_save($key, $value, $ttl = 300)
{
	$CI = &get_instance();
	if (isset($CI->cache)) {
		$CI->cache->save($key, $value, $ttl);
	}
}

function can_action($menu_id, $action)
{
	$CI = &get_instance();
	$designations_id = $CI->session->userdata('designations_id');
	$where = array('designations_id' => $designations_id, $action => $menu_id);
	$user_type = $CI->session->userdata('user_type');
	if ($user_type == 1) {
		return true;
	} else {
		$cacheKey = 'can_action_' . $designations_id . '_' . $menu_id . '_' . $action;
		$cached = _role_cache_get($cacheKey);
		if ($cached !== false) {
			return $cached;
		}
		$can_do = $CI->db->where($where)->get('tbl_user_role')->row();
		$result = !empty($can_do);
		_role_cache_save($cacheKey, $result);
		return $result;
	}
}

function can_action_by_label($label, $action)
{
	$CI = &get_instance();
	$menu_cache_key = 'menu_id_by_label_' . $label;
	$menu_id = _role_cache_get($menu_cache_key);
	if ($menu_id === false) {
		$menu_id = get_any_field('tbl_menu', array('label' => $label), 'menu_id');
		_role_cache_save($menu_cache_key, $menu_id);
	}
	$designations_id = $CI->session->userdata('designations_id');
	$where = array('designations_id' => $designations_id, $action => $menu_id);
	$user_type = $CI->session->userdata('user_type');
	if ($user_type == 1) {
		return true;
	} else {
		$cacheKey = 'can_action_label_' . $designations_id . '_' . $label . '_' . $action;
		$cached = _role_cache_get($cacheKey);
		if ($cached !== false) {
			return $cached;
		}
		$can_do = $CI->db->where($where)->get('tbl_user_role')->row();
		$result = !empty($can_do);
		_role_cache_save($cacheKey, $result);
		return $result;
	}
}

function can_do($menu_id)
{
	$CI = &get_instance();
	$designations_id = $CI->session->userdata('designations_id');
	$user_type = $CI->session->userdata('user_type');
	if ($user_type == 1) {
		return true;
	} else {
		$cacheKey = 'can_do_' . $designations_id . '_' . $menu_id;
		$cached = _role_cache_get($cacheKey);
		if ($cached !== false) {
			return $cached;
		}
		$can_do = $CI->db->where(array('designations_id' => $designations_id, 'menu_id' => $menu_id))->get('tbl_user_role')->result();
		$result = !empty($can_do);
		_role_cache_save($cacheKey, $result);
		return $result;
	}
}

function value_exists_in_array_by_key($array, $key, $val)
{
	foreach ($array as $item) {
		if (isset($item[$key]) && $item[$key] == $val) {
			return true;
		}
	}
	return false;
}

function clear_textarea_breaks($text)
{
	$_text = '';
	$_text = $text;
	$breaks = array(
		"<br />",
		"<br>",
		"<br/>",
		"'",
		'"',
		"-",
		"^",
		"/",
		"%",
	);
	$_text = str_ireplace($breaks, "", $_text);
	$_text = trim($_text);
	return $_text;
}

function set_mysql_timezone($timezone)
{
	$offset = timezone_offset_get(new DateTimeZone($timezone), new DateTime());
	$sign = ($offset > 0) ? '+' : '-';
	$offset = gmdate('H:i', abs($offset));
	$zone = $sign . $offset;
	$CI = &get_instance();
	$CI->db->query("SET time_zone='$zone'");
	return true;
}

function access_denied($permission = '', $module = null)
{
	$CI = &get_instance();
	set_message('danger', lang('access_denied'));
	$activity = array(
		'user' => $CI->session->userdata('user_id'),
		'module' => $module,
		'module_field_id' => $CI->session->userdata('user_id'),
		'activity' => 'access_denied',
		'value1' => 'Tried to access page where don\'t have permission [' . $permission . ']',
	);


	$CI->load->model('admin_model');
	$CI->admin_model->_table_name = 'tbl_activities';
	$CI->admin_model->_primary_key = 'activities_id';
	$CI->admin_model->save($activity);

	if (isset($_SERVER['HTTP_REFERER']) && !empty($_SERVER['HTTP_REFERER'])) {
		redirect($_SERVER['HTTP_REFERER']);
	} else {
		redirect('404');
	}
}

function check_installation()
{
	if (is_dir(FCPATH . 'install')) {
		echo '<h3>Delete the install folder</h3>';
		die;
	}
}

/**
 * Function that will replace the dropbox link size for the images
 * This function is used to preview dropbox image attachments
 * @param string $url
 * @param string $bounding_box
 * @return string
 */
function optimize_dropbox_thumbnail($url, $bounding_box = '800')
{
	$url = str_replace('bounding_box=75', 'bounding_box=' . $bounding_box, $url);

	return $url;
}

function protected_file_url_by_path($path)
{
	return str_replace(FCPATH, '', $path);
}

function _mime_content_type($filename)
{
	if (function_exists('mime_content_type'))
		return mime_content_type($filename);
	else if (function_exists('finfo_open')) {
		$finfo = finfo_open(FILEINFO_MIME);
		$mimetype = finfo_file($finfo, $filename);
		return $mimetype;
	} else
		return get_mime_by_extension($filename);
}

/**
 * Add user notifications
 * @param array $values array of values [description,from_user_id,to_user_id,is_read]
 */
function add_notification($values)
{
	$CI = &get_instance();
	foreach ($values as $key => $value) {
		$data[$key] = $value;
	}
	if (!empty($data['from_user_id'])) {
		$user_id = $CI->session->userdata('user_id');
		$data['from_user_id'] = $user_id;
		$data['name'] = $CI->db->where('user_id', $user_id)->get('tbl_account_details')->row()->fullname;
	}
	// Prevent sending notification to non active users.
	if (isset($data['to_user_id']) && $data['to_user_id'] != 0) {
		$CI->db->where('user_id', $data['to_user_id']);
		$user = $CI->db->get('tbl_users')->row();
		if (!$user) {
			return false;
		}
		if ($user) {
			if ($user->activated == 0) {
				return false;
			}
		}
	}
	$data['date'] = date('Y-m-d H:i:s');
	$CI->db->insert('tbl_notifications', $data);
	return true;
}

function profile($id = null)
{
	$CI = &get_instance();
	static $profile_cache = array();
	if (empty($id)) {
		$id = $CI->session->userdata('user_id');
	}
	if (!empty($id)) {
		if (array_key_exists($id, $profile_cache)) {
			return $profile_cache[$id];
		}
		$cacheKey = 'profile_' . (int) $id;
		$cached = helper_cache_get($cacheKey);
		if ($cached !== false) {
			$profile_cache[$id] = $cached;
			return $cached;
		}
		$profile = $CI->db
			->where("tbl_users.user_id", $id)
			->join("tbl_account_details", "tbl_account_details.user_id = tbl_users.user_id")
			->get("tbl_users")->row();
		$profile_cache[$id] = $profile;
		if (!empty($profile)) {
			helper_cache_save($cacheKey, $profile, 300);
		}
		return $profile;
	} else {
		return false;
	}
}

function my_id()
{
	$CI = &get_instance();
	return $CI->session->userdata('user_id');
}

function admin($user_id = null)
{
	$CI = &get_instance();
	if (empty($user_id)) {
		$user_id = $CI->session->userdata('user_id');
	}
	$role_id = get_any_field('tbl_users', array('user_id' => $user_id), 'role_id');
	if ($role_id == 1) {
		return true;
	} else {
		return false;
	}
}

function staff($user_id = null)
{
	$CI = &get_instance();
	if (empty($user_id)) {
		$user_id = $CI->session->userdata('user_id');
	}
	$role_id = get_any_field('tbl_users', array('user_id' => $user_id), 'role_id');
	if ($role_id == 3) {
		return true;
	} else {
		return false;
	}
}

function client($user_id = null)
{
	$CI = &get_instance();
	if (empty($user_id)) {
		$user_id = $CI->session->userdata('user_id');
	}
	$role_id = get_any_field('tbl_users', array('user_id' => $user_id), 'role_id');
	if ($role_id == 2) {
		return true;
	} else {
		return false;
	}
}

function all_admin()
{
	$CI = &get_instance();
	$all_admin = $CI->db->where('role_id', 1)->order_by('sort_user', 'ASC')->get('tbl_users')->result();
	if (!empty($all_admin)) {
		return $all_admin;
	} else {
		return false;
	}
}

function get_admin_number()
{
	$CI = &get_instance();
	$admin = $CI->db
		->where("tbl_users.role_id", 1)
		->join("tbl_account_details", "tbl_account_details.user_id = tbl_users.user_id")
		->get("tbl_users")->row();
	if (!empty($admin)) {
		return $admin->mobile;
	} else {
		return false;
	}
}

function department_head()
{
	$CI = &get_instance();
	$designations_id = $CI->session->userdata('designations_id');
	//    get_row('tbl_designations',array());
	$designation = $CI->db->where('designations_id', $designations_id)->get('tbl_designations')->row();
	if (!empty($designation)) {
		$department = $CI->db->where('departments_id', $designation->departments_id)->get('tbl_departments')->row();
	}
	if (!empty($department->department_head_id) && $department->department_head_id == my_id()) {
		return true;
	} else {
		return false;
	}
}

function total_rows($table, $where = [])
{
	$CI = &get_instance();
	if (is_array($where)) {
		if (sizeof($where) > 0) {
			$CI->db->where($where);
		}
	} elseif (strlen($where) > 0) {
		$CI->db->where($where);
	}

	return $CI->db->count_all_results($table);
}

function staffImage($user_id = null)
{
	$CI = &get_instance();
	if (empty($user_id)) {
		$user_id = $CI->session->userdata('user_id');
	}
	$userInfo = $CI->db->where('user_id', $user_id)->get('tbl_account_details')->row();
	if (!empty($userInfo) && file_exists($userInfo->avatar)) {
		return $userInfo->avatar;
	} else {
		return 'assets/img/user/default_avatar.jpg';
	}
}

function fullname($user_id = null)
{
	$CI = &get_instance();
	if (empty($user_id)) {
		$user_id = $CI->session->userdata('user_id');
	}
	$userInfo = $CI->db->where('user_id', $user_id)->get('tbl_account_details')->row();
	if (!empty($userInfo)) {
		return $userInfo->fullname;
	} else {
		return 'Undefined user';
	}
}

function client_contact_email($client_id = null)
{
	$client = get_row('tbl_client', array('client_id' => $client_id));
	if (!empty($client)) {
		$userInfo = get_staff_details($client->primary_contact);
		if (!empty($userInfo) && is_object($userInfo)) {
			return $userInfo->email;
		} else {
			return $client->email;
		}
	}
}

function timer_status($module, $id, $status, $get_id = null)
{
	$CI = &get_instance();
	$user_id = $CI->session->userdata('user_id');
	if ($module == 'tasks') {
		$where = array('task_id' => $id, 'user_id' => $user_id, 'timer_status' => $status);
	} else {
		$where = array('project_id' => $id, 'user_id' => $user_id, 'timer_status' => $status);
	}
	$result = $CI->db->where($where)->order_by('tasks_timer_id', 'DESC')->get('tbl_tasks_timer')->row();
	if (!empty($result)) {
		if (!empty($get_id)) {
			return $result->tasks_timer_id;
		} else {
			return $result->timer_status;
		}
	} else {
		return false;
	}
}

function client_name($client_id = null)
{
	$CI = &get_instance();
	if (empty($client_id)) {
		$client_id = $CI->session->userdata('client_id');
	}
	if (is_numeric($client_id)) {
		$clientInfo = $CI->db->where('client_id', $client_id)->get('tbl_client')->row();
	}
	if (!empty($clientInfo)) {
		return $clientInfo->name;
	} else {
		return lang('undefined_client');
	}
}

function client_id()
{
	$CI = &get_instance();
	$client_id = $CI->session->userdata('client_id');
	return $client_id;
}

function is_department_head($id = null)
{
	$CI = &get_instance();
	if (empty($id)) {
		$id = $CI->session->userdata('user_id');
	}
	$cacheKey = 'is_department_head_' . $id;
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}
	$department_head_id = $CI->db->where('department_head_id', $id)->get('tbl_departments')->row();
	$result = !empty($department_head_id);
	helper_cache_save($cacheKey, $result, 900);
	return $result;
}

function designation($id = null)
{
	$CI = &get_instance();
	if (empty($id)) {
		$id = $CI->session->userdata('user_id');
	}
	$cacheKey = 'designation_' . $id;
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}
	$userInfo = $CI->db->where('user_id', $id)->get('tbl_account_details')->row();
	$designation = '-';
	if (!empty($userInfo->designations_id)) {
		$row = $CI->db->where('designations_id', $userInfo->designations_id)->get('tbl_designations')->row();
		if (!empty($row)) {
			$designation = $row->designations;
		}
	}
	helper_cache_save($cacheKey, $designation, 900);
	return $designation;
}

/**
 *
 * @param  $array - data
 * @param  $key - value you want to pluck from array
 *
 * @return plucked array only with key data
 */
if (!function_exists('array_pluck')) {
	function array_pluck($array, $key)
	{
		return array_map(function ($v) use ($key) {
			return is_object($v) ? $v->$key : $v[$key];
		}, $array);
	}
}

/**
 * Short Time ago function
 * @param datetime $time_ago
 * @return mixed
 */
function time_ago($time_ago)
{
	if (is_numeric($time_ago) && (int)$time_ago == $time_ago) {
		$time_ago = $time_ago;
	} else {
		$time_ago = safe_strtotime($time_ago);
	}
	$cur_time = time();
	$time_elapsed = $cur_time - $time_ago;
	$seconds = $time_elapsed;
	$minutes = round($time_elapsed / 60);
	$hours = round($time_elapsed / 3600);
	$days = round($time_elapsed / 86400);
	$weeks = round($time_elapsed / 604800);
	$months = round($time_elapsed / 2600640);
	$years = round($time_elapsed / 31207680);
	// Seconds
	if ($seconds <= 60) {
		return lang('time_ago_just_now');
	} //Minutes
	elseif ($minutes <= 60) {
		if ($minutes == 1) {
			return lang('time_ago_minute');
		} else {
			return lang('time_ago_minutes', $minutes);
		}
	} //Hours
	elseif ($hours <= 24) {
		if ($hours == 1) {
			return lang('time_ago_hour');
		} else {
			return lang('time_ago_hours', $hours);
		}
	} //Days
	elseif ($days <= 7) {
		if ($days == 1) {
			return lang('time_ago_yesterday');
		} else {
			return lang('time_ago_days', $days);
		}
	} //Weeks
	elseif ($weeks <= 4.3) {
		if ($weeks == 1) {
			return lang('time_ago_week');
		} else {
			return lang('time_ago_weeks', $weeks);
		}
	} //Months
	elseif ($months <= 12) {
		if ($months == 1) {
			return lang('time_ago_month');
		} else {
			return lang('time_ago_months', $months);
		}
	} //Years
	else {
		if ($years == 1) {
			return lang('time_ago_year');
		} else {
			return lang('time_ago_years', $years);
		}
	}
}

function daysleft($time)
{
	$result = null;
	$to_date = safe_strtotime($time); //Future date.
	$cur_date = safe_strtotime(date('Y-m-d'));
	$timeleft = $to_date - $cur_date;
	$daysleft = round((($timeleft / 24) / 60) / 60);
	if ($daysleft == 1) {
		$result = $daysleft . ' ' . lang('day') . ' ' . lang('left');
	} else if ($daysleft > 1) {
		$result = $daysleft . ' ' . lang('days') . ' ' . lang('left');
	} else if ($daysleft == -1) {
		$result = $daysleft . ' ' . lang('day') . ' ' . lang('gone');
	} else if ($daysleft > -1) {
		$result = $daysleft . ' ' . lang('days') . ' ' . lang('gone');
	}
	return $result;
}

/**
 * check post file is valid or not
 *
 * @param string $file_name
 * @return json data of success or error message
 */
if (!function_exists('validate_post_file')) {

	function validate_post_file($file_name = "")
	{
		if (is_valid_file_to_upload($file_name)) {
			echo json_encode(array("success" => true));
			exit();
		} else {
			echo json_encode(array("success" => false, 'message' => lang('invalid_file_type') . " ($file_name)"));
			exit();
		}
	}
}

/**
 * this method process 3 types of files
 * 1. direct upload
 * 2. move a uploaded file which has been uploaded in temp folder
 * 3. copy a text based image
 *
 * @param string $file_name
 * @param string $target_path
 * @param string $source_path
 * @param string $static_file_name
 * @return filename
 */
if (!function_exists('unique_filename')) {
	function unique_filename($target_path, $file_name)
	{
		$target_path = rtrim($target_path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
		$clean = basename($file_name);
		$clean = preg_replace('/[^A-Za-z0-9._-]/', '_', $clean);
		$info = pathinfo($clean);
		$base = $info['filename'] ?? 'file';
		$ext = isset($info['extension']) && $info['extension'] !== '' ? '.' . $info['extension'] : '';

		$candidate = $base . $ext;
		$counter = 0;
		while (file_exists($target_path . $candidate)) {
			$counter++;
			$candidate = $base . '-' . $counter . $ext;
			if ($counter > 1000) {
				$candidate = $base . '-' . uniqid() . $ext;
				break;
			}
		}
		return $candidate;
	}
}

if (!function_exists('move_temp_file')) {

	function move_temp_file($file_name, $target_path, $related_to = "", $source_path = NULL, $static_file_name = "")
	{
		$new_filename = unique_filename($target_path, $file_name);
		//if not provide any source path we'll fi   nd the default path
		if (!$source_path) {
			$source_path = getcwd() . "/uploads/temp/" . $file_name;
		}

		//check destination directory. if not found try to create a new one
		if (!is_dir($target_path)) {
			if (!mkdir($target_path, 0777, true)) {
				die('Failed to create file folders.');
			}
		}

		//overwrite extisting logic and use static file name
		if ($static_file_name) {
			$new_filename = $static_file_name;
		}

		//check the file type is data or file. then copy to destination and remove temp file
		if (starts_with($source_path, "data")) {
			copy_text_based_image($source_path, $target_path . $new_filename);
			return $new_filename;
		} else {
			if (file_exists($source_path)) {
				copy($source_path, $target_path . $new_filename);
				unlink($source_path);
				return $new_filename;
			}
		}
		return false;
	}
}
/**
 * Convert to a file from text based image
 *
 * @param string $source_path
 * @param string $target_path
 * @return file size
 */
if (!function_exists('copy_text_based_image')) {

	function copy_text_based_image($source_path, $target_path)
	{
		$buffer_size = 3145728;
		$byte_number = 0;
		$file_open = fopen($source_path, "rb");
		$file_wirte = fopen($target_path, "w");
		while (!feof($file_open)) {
			$byte_number += fwrite($file_wirte, fread($file_open, $buffer_size));
		}
		fclose($file_open);
		fclose($file_wirte);
		return $byte_number;
	}
}
/**
 * check if a string starts with a specified sting
 *
 * @param string $string
 * @param string $needle
 * @return true/false
 */
if (!function_exists('starts_with')) {

	function starts_with($string, $needle)
	{
		$string = $string;
		return $needle === "" || strrpos($string, $needle, -strlen($string)) !== false;
	}
}

/**
 * check the file type is valid for upload
 *
 * @param string $file_name
 * @return true/false
 */
if (!function_exists('is_valid_file_to_upload')) {

	function is_valid_file_to_upload($file_name = "")
	{

		if (!$file_name)
			return false;

		$file_ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));

		$file_formates = explode('|', config_item('allowed_files'));
		if (in_array($file_ext, $file_formates)) {
			return true;
		}
	}
}
/**
 * Validate the image
 *
 * @return    bool
 */
function check_image_extension($image)
{
	$images_extentions = array("jpg", "JPG", "jpeg", "JPEG", "png", "PNG", "gif", "GIF", "bmp", "BMP");
	$image_parts = explode(".", $image);
	$image_end_part = end($image_parts);

	if (in_array($image_end_part, $images_extentions) == true) {
		return 1;
	} else {
		return 0;
	}
}


/**
 * Get all files/folder from dir in an array
 * @param string $dir directory full path
 * @param array  &$results
 * @return array
 */
function get_dir_contents($dir, &$results = [])
{
	$files = scandir($dir);

	foreach ($files as $key => $value) {
		$path = realpath($dir . DIRECTORY_SEPARATOR . $value);
		if (!is_dir($path)) {
			$results[] = $path;
		} elseif ($value != '.' && $value != '..') {
			get_dir_contents($path, $results);
			$results[] = $path;
		}
	}

	return $results;
}

/**
 * upload a file to temp folder when using dropzone autoque=true
 *
 * @param file $_FILES
 * @return void
 */
if (!function_exists('upload_file_to_temp')) {

	function upload_file_to_temp()
	{
		if (!empty($_FILES)) {
			$temp_file = $_FILES['file']['tmp_name'];
			$file_name = $_FILES['file']['name'];
			$file_size = $_FILES['file']['size'];

			if (!is_valid_file_to_upload($file_name))
				return array('success' => false, 'message' => lang('invalid_file_type'));

			$target_path = getcwd() . '/uploads/temp/';
			if (!is_dir($target_path)) {
				if (!mkdir($target_path, 0777, true)) {
					return array('success' => false, 'message' => lang('dir_not_found'));
				}
			}
			// cleanup temp files older than 24 hours
			$expire_after = 60 * 60 * 24;
			foreach (glob($target_path . '*') as $tmp) {
				if (is_file($tmp) && (time() - filemtime($tmp)) > $expire_after) {
					@unlink($tmp);
				}
			}
			$ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
			$safe_ext = $ext ? '.' . preg_replace('/[^a-z0-9]/', '', $ext) : '';
			$temp_name = 'tmp_' . bin2hex(random_bytes(8)) . $safe_ext;
			$target_file = $target_path . $temp_name;
			if (!move_uploaded_file($temp_file, $target_file)) {
				return array('success' => false, 'message' => lang('file_upload_failed'));
			}
			return array(
				'success' => true,
				'temp_name' => $temp_name,
				'file_name' => $file_name,
				'file_size' => $file_size,
			);
		}
		return array('success' => false, 'message' => lang('file_upload_failed'));
	}
}
/**
 * Supported html5 video extensions
 * @return array
 */
function get_html5_video_extensions()
{
	return array(
		'mp4',
		'm4v',
		'webm',
		'ogv',
		'ogg',
		'flv'
	);
}

/**
 * Check if filename/path is video file
 * @param string $path
 * @return boolean
 */
function is_html5_video($path)
{
	$ext = _get_file_extension($path);
	if (in_array($ext, get_html5_video_extensions())) {
		return true;
	}
	return false;
}

/**
 * Get file extension by filename
 * @param string $file_name file name
 * @return mixed
 */
function _get_file_extension($file_name)
{
	return substr(strrchr($file_name, '.'), 1);
}

/**
 * Function used to validate all recaptcha from google reCAPTCHA feature
 * @param string $str
 * @return boolean
 */
function do_recaptcha_validation($str = '')
{
	$CI = &get_instance();
	$CI->load->library('form_validation');
	$google_url = "https://www.google.com/recaptcha/api/siteverify";
	$secret = config_item('recaptcha_secret_key');
	$ip = $CI->input->ip_address();
	$url = $google_url . "?secret=" . $secret . "&response=" . $str . "&remoteip=" . $ip;
	$curl = curl_init();
	curl_setopt($curl, CURLOPT_URL, $url);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($curl, CURLOPT_TIMEOUT, 10);
	$res = curl_exec($curl);
	curl_close($curl);
	$res = json_decode($res, true);
	//reCaptcha success check
	if ($res['success']) {
		return true;
	} else {
		$CI->form_validation->set_message('recaptcha', lang('recaptcha_error'));
		return false;
	}
}

function MyDetails($user_id = null)
{
	$CI = &get_instance();
	if (empty($user_id)) {
		$user_id = $CI->session->userdata('user_id');
	}
	$cacheKey = 'my_details_' . $user_id;
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}
	$CI->db->select('tbl_users.*', FALSE);
	$CI->db->select('tbl_account_details.*', FALSE);
	$CI->db->select('tbl_designations.departments_id', FALSE);
	$CI->db->from('tbl_users');
	$CI->db->join('tbl_account_details', 'tbl_users.user_id = tbl_account_details.user_id', 'left');
	$CI->db->join('tbl_designations', 'tbl_designations.designations_id = tbl_account_details.designations_id', 'left');
	$CI->db->where('tbl_users.user_id', $user_id);
	//    $CI->db->where('tbl_users.role_id !=', 2);
	//    $CI->db->where('tbl_users.activated', 1);
	$query_result = $CI->db->get();
	$result = $query_result->row();
	helper_cache_save($cacheKey, $result, 600);
	return $result;
}

function get_staff_details($user_id = null, $type = null, $where = null)
{
	$CI = &get_instance();
	$cacheKey = 'staff_details_' . md5(json_encode(array($user_id, $type, $where)));
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}
	$CI->db->select('tbl_users.*', FALSE);
	$CI->db->select('tbl_account_details.*', FALSE);
	$CI->db->select('tbl_designations.designations', FALSE);

	$CI->db->from('tbl_users');
	$CI->db->join('tbl_account_details', 'tbl_users.user_id = tbl_account_details.user_id', 'left');
	$CI->db->join('tbl_designations', 'tbl_designations.designations_id = tbl_account_details.designations_id', 'left');
	if (!empty($where)) {
		$CI->db->where($where);
	}
	if (!empty($user_id)) {
		$CI->db->where('tbl_users.user_id', $user_id);
		$query_result = $CI->db->get();
		$result = $query_result->row();
	} else {
		// check if where have role id condition to exclude client
		if (is_array($where)) {
			$has_role_id = array_key_exists('role_id', $where);
		} else {
			$has_role_id = false;
		}
		if (!$has_role_id) {
			$CI->db->where('tbl_users.role_id !=', 2);
		}
		$CI->db->where('tbl_users.activated', 1);
		//sort_user
		$CI->db->order_by('tbl_users.sort_user', 'ASC');
		$query_result = $CI->db->get();
		if (!empty($type)) {
			$result = $query_result->result_array();
		} else {
			$result = $query_result->result();
		}
	}
	helper_cache_save($cacheKey, $result, 600);
	return $result;
}


/**
 * prepare a anchor tag for ajax request
 *
 * @param string $url
 * @param string $title
 * @param array $attributes
 * @return html link of anchor tag
 */
if (!function_exists('ajax_anchor')) {

	function ajax_anchor($url, $title = '', $attributes = '')
	{
		if (is_array($attributes)) {
			$existing_class = !empty($attributes['class']) ? trim($attributes['class']) . ' ' : '';
			$attributes = array_merge(array(
				'class' => $existing_class . 'js-ajax-delete',
				'data-act' => 'ajax-request',
				'data-action-url' => $url,
				'data-confirm-action' => 'delete',
				'data-confirm-title' => lang('delete'),
				'data-confirm-accept-label' => lang('delete'),
				'data-confirm-message' => lang('delete_alert'),
			), $attributes);
		} else {
			$attributes .= ' class="js-ajax-delete" data-act="ajax-request" data-action-url="' . $url . '" data-confirm-action="delete" data-confirm-title="' . lang('delete') . '" data-confirm-accept-label="' . lang('delete') . '" data-confirm-message="' . lang('delete_alert') . '"';
		}

		return action_btn($url, $title, 'danger', 'trash-2', 'xs', $attributes);
	}
}

/**
 * prepare a anchor tag for any js request
 *
 * @param string $title
 * @param array $attributes
 * @return html link of anchor tag
 */
if (!function_exists('js_anchor')) {

	function js_anchor($title = '', $attributes = '')
	{
		$title = (string)$title;

		$html_attributes = "";
		if (is_array($attributes)) {
			foreach ($attributes as $key => $value) {
				$html_attributes .= ' ' . $key . '="' . $value . '"';
			}
		}
		return '<strong data-toggle="tooltip" data-placement="top" style="cursor:pointer"' . $html_attributes . '>' . $title . '</strong>';
	}
}
/**
 * prepare a anchor tag for any js request
 *
 * @param string $title
 * @param array $attributes
 * @return html link of anchor tag
 */
if (!function_exists('remove_files')) {

	function remove_files($fileName, $dir = null)
	{
		//Delete the file
		if (empty($dir)) {
			$dir = 'uploads/';
		}
		if (is_file($dir . $fileName)) {
			unlink($dir . $fileName);
		}
		return true;
	}
}
function get_result($tbl, $where = null, $type = null)
{
	$CI = &get_instance();
	$cacheKey = 'get_result_' . $tbl . '_' . md5(json_encode(array($where, $type, isset($_POST["length"]) ? $_POST["length"] : null, isset($_POST["start"]) ? $_POST["start"] : null)));
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}
	$CI->db->select('*');
	$CI->db->from($tbl);
	if (!empty($where) && $where != 0) {
		$CI->db->where($where);
	}
	if (!empty($_POST["length"]) && $_POST["length"] != -1) {
		$CI->db->limit($_POST['length'], $_POST['start']);
	}
	$query_result = $CI->db->get();
	if (!empty($type) && $type == 'array') {
		$result = $query_result->result_array();
	} else if (!empty($type)) {
		$result = $query_result->row();
	} else {
		$result = $query_result->result();
	}
	helper_cache_save($cacheKey, $result, 300);
	return $result;
}

function get_order_by($tbl, $where = null, $order_by = null, $ASC = null, $limit = null)
{
	$CI = &get_instance();
	$cacheKey = 'get_order_by_' . $tbl . '_' . md5(json_encode(array($where, $order_by, $ASC, $limit)));
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}
	$CI->db->from($tbl);
	if (!empty($where) && $where != 0) {
		$CI->db->where($where);
	}
	if (!empty($ASC)) {
		$order = 'ASC';
	} else {
		$order = 'DESC';
	}
	$CI->db->order_by($order_by, $order);
	if (!empty($limit)) {
		$CI->db->limit($limit);
	}
	$query_result = $CI->db->get();
	$result = $query_result->result();
	helper_cache_save($cacheKey, $result, 300);
	return $result;
}


function read_more($str, $limit, $url, $attachment = null)
{
	// strip tags to avoid breaking any html
	$string = strip_html_tags($str, true);
	if (strlen($string) > $limit) {
		// truncate string
		$stringCut = substr($string, 0, $limit);
		// make sure it ends in a word so assassinate doesn't become ass...
		$string = substr($stringCut, 0, strrpos($stringCut, ' ')) . '... <a href="' . base_url($url) . '">' . lang('read_more') . '</a>';
	} elseif (!empty($attachment)) {
		$string = $string . '... <a href="' . base_url($url) . '">' . lang('read_more') . '</a>';
	} else {
		$string = $str;
	}
	return $string;
}


if (!function_exists('cal_days_in_month')) {
	function cal_days_in_month($calendar, $month, $year)
	{
		return date('t', mktime(0, 0, 0, $month, 1, $year));
	}
}
if (!defined('CAL_GREGORIAN'))
	define('CAL_GREGORIAN', 1);


function calculate_taken_leave($token_leave)
{
	$CI = &get_instance();
	$office_hours = config_item('office_hours');
	$total_taken = 0;
	$total_hourly = 0;
	$allow_weekend_excluded_from_leave = config_item('allow_weekend_excluded_from_leave');
	if ($allow_weekend_excluded_from_leave == 'TRUE') {
		$holidays = $CI->common_model->get_holidays(); // get weekends column day
		foreach ($token_leave as $key => $v_leave) {
			if ($v_leave->leave_type != 'hours') {
				if (empty($v_leave->leave_end_date)) {
					$v_leave->leave_end_date = $v_leave->leave_start_date;
				}
				$allDates = $CI->common_model->GetDays($v_leave->leave_start_date, $v_leave->leave_end_date);
				foreach ($allDates as $key => $value) {
					$dayName = date("l", safe_strtotime($value));
					if (!in_array($dayName, array_column($holidays, 'day'))) {
						$total_taken++;
					}
				}
			} else {
				$dayName = date("l", safe_strtotime($v_leave->leave_start_date));
				if (!in_array($dayName, array_column($holidays, 'day'))) {
					$total_hourly += ($v_leave->hours / $office_hours);
				}
			}
		}
	} else {
		foreach ($token_leave as $key => $v_leave) {
			if ($v_leave->leave_type != 'hours') {
				if (empty($v_leave->leave_end_date)) {
					$v_leave->leave_end_date = $v_leave->leave_start_date;
				}
				$allDates = $CI->common_model->GetDays($v_leave->leave_start_date, $v_leave->leave_end_date);
				foreach ($allDates as $key => $value) {
					$total_taken++;
				}
			} else {
				$total_hourly += ($v_leave->hours / $office_hours);
			}
		}
	}
	$data['total_taken'] = $total_taken;
	$data['total_hourly'] = $total_hourly;
	return $data;
}

function leave_report($id = null)
{
	$CI = &get_instance();
	$office_hours = config_item('office_hours');
	$all_category = $CI->db->get('tbl_leave_category')->result();
	$cacheKey = 'leave_report_' . ($id ?: 'all') . '_' . date('Y');
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}
	$result = array();
	foreach ($all_category as $v_category) {
		if (!empty($id)) {
			$where = array('user_id' => $id, 'YEAR(`leave_start_date`)' => date('Y'), 'leave_category_id' => $v_category->leave_category_id, 'application_status' => 2);
		} else {
			$where = array('YEAR(`leave_start_date`)' => date('Y'), 'leave_category_id' => $v_category->leave_category_id, 'application_status' => 2);
		}
		$all_leave_info = $CI->db->where($where)->get('tbl_leave_application')->result();

		$total_days = 0;
		$total_hours = 0;
		if (!empty($all_leave_info)) {
			$res = calculate_taken_leave($all_leave_info);

			$total_days = $res['total_taken'];
			$total_hours = $res['total_hourly'];

			if (empty($total_days)) {
				$total_days = 0;
			}
			if (empty($total_hours)) {
				$total_hours = 0;
			} else {
				$total_hours = number_format($total_hours, 2);
			}
		}
		$result['leave_category'][] = $v_category->leave_category;
		$result['leave_quota'][] = $v_category->leave_quota;
		$result['leave_taken'][] = $total_days + $total_hours;
	}
	helper_cache_save($cacheKey, $result, 600);
	return $result;
}

function get_any_field($table, $where, $table_field, $result = null)
{
	$CI = &get_instance();
	$query = $CI->db->select($table_field)->where($where)->get($table);
	if ($query->num_rows() > 0) {
		if (!empty($result)) {
			return $query->result_array();
		} else {
			$row = $query->row();
			return $row->$table_field;
		}
	}
}

function get_row($table, $where, $fields = null)
{
	$CI = &get_instance();
	$query = $CI->db->where($where)->get($table);
	if ($query->num_rows() > 0) {
		$row = $query->row();
		if (!empty($fields)) {
			return $row->$fields;
		} else {
			return $row;
		}
	}
}

function update($table, $where, $data)
{
	$CI = &get_instance();
	$CI->db->where($where);
	$CI->db->update($table, $data);
}

function delete($table, $where)
{
	$CI = &get_instance();
	$CI->db->where($where);
	$CI->db->delete($table);
}

function convert_currency($currency, $amount)
{
	if (empty($currency)) {
		return $amount;
	}
	if ($currency == config_item('default_currency')) {
		return $amount;
	}
	$CI = &get_instance();
	$currency_info = $CI->db->where('code', config_item('default_currency'))->get('tbl_currencies')->row();
	if ($currency_info->xrate > 0) {
		$in_local_cur = $amount * $currency_info->xrate;
		$convert_currency = $CI->db->where('code', $currency)->get('currencies')->row();
		$in_local = $in_local_cur / $convert_currency->xrate;
		return $in_local;
	} else {
		return $amount;
	}
}

// function make_datatables($where = null, $where_in = null)
// {
//     $CI = &get_instance();
//     $CI->load->model('datatables');
//     $CI->datatables->make_query();

//     if (!empty($where)) {
//         $CI->db->where($where);
//     }
//     if (!empty($where_in)) {        
//         $CI->db->where_in($where_in[0], $where_in[1]);
//     }
//     if ($_POST["length"] != -1) {
//         $CI->db->limit($_POST['length'], $_POST['start']);
//     }
//     $query = $CI->db->get();
//     return $query->result();
// }

function make_datatables($where = null, $where_in = null)
{
	$CI = &get_instance();
	$CI->load->model('datatables');

	// Get page length and start (robust defaults if JS doesn't send)
	$length = isset($_POST['length']) ? (int)$_POST['length'] : 25;
	$start  = isset($_POST['start'])  ? (int)$_POST['start']  : 0;

	return $CI->datatables->get_rows($where, $where_in, $length, $start);
}

// function render_table($data, $where = null, $where_in = null)
// {

//     $CI = &get_instance();
//     $CI->load->model('datatables');
//     $output = array(
//         "draw" => intval($_POST["draw"]),
//         "recordsTotal" => $CI->datatables->get_all_data($where, $where_in),
//         "recordsFiltered" => $CI->datatables->get_filtered_data($where, $where_in),
//         "data" => $data
//     );
//     echo json_encode($output);
//     exit();
// }

/**
 * Faster renderer:
 * - Does NOT trigger extra full queries itself.
 * - Asks the Datatables model for count_all() and count_filtered() (lightweight).
 * - Outputs standard JSON that DataTables expects.
 *
 * Signature kept the same so existing calls like render_table($data,$where) don't break.
 */
function build_table_output($data, $where = null, $where_in = null)
{
	$CI = &get_instance();
	$CI->load->model('datatables');

	$total    = $CI->datatables->get_all_data($where, $where_in);
	$filtered = $CI->datatables->get_filtered_data($where, $where_in);

	return array(
		"draw" => intval(isset($_POST["draw"]) ? $_POST["draw"] : 1),
		"recordsTotal" => $total,
		"recordsFiltered" => $filtered,
		"data" => $data
	);
}

function render_table($data, $where = null, $where_in = null)
{
	$CI = &get_instance();
	$output = build_table_output($data, $where, $where_in);
	if (!empty($CI->capture_render_table)) {
		$CI->last_render_table_output = $output;
		return $output;
	}
	echo json_encode($output);
	exit();
}


function default_currency()
{
	$CI = &get_instance();
	$cacheKey = 'default_currency_' . config_item('default_currency');
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}
	$currency_info = $CI->db->where('code', config_item('default_currency'))->get('tbl_currencies')->row();
	$result = empty($currency_info) ? '$' : $currency_info->symbol;
	helper_cache_save($cacheKey, $result, 1800);
	return $result;
}

function get_all_client_currencies()
{
	$_CI = &get_instance();
	$_CI->load->database();
	$cacheKey = 'all_client_currencies_map';
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}
	$currencies_res = $_CI->db->query("SELECT tbl_client.client_id, tbl_currencies.symbol
    FROM tbl_client
    LEFT JOIN tbl_currencies ON tbl_currencies.code = tbl_client.currency")->result_array();
	$result = (array_column($currencies_res, 'symbol', 'client_id'));
	helper_cache_save($cacheKey, $result, 1800);
	return $result;
}

// get all countries from tbl_countries
function get_all_countries()
{
	$CI = &get_instance();
	$cacheKey = 'all_countries_list';
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}
	$countries = $CI->db->order_by('value', 'ASC')->get('tbl_countries')->result();
	helper_cache_save($cacheKey, $countries, 86400);
	return $countries;
}

// get all currencies from tbl_currencies
function get_all_currencies()
{
	$CI = &get_instance();
	$cacheKey = 'all_currencies_list';
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}
	$currencies = $CI->db->order_by('code', 'ASC')->get('tbl_currencies')->result();
	helper_cache_save($cacheKey, $currencies, 86400);
	return $currencies;
}


function client_currency($id)
{
	if ($id === null || $id === '') {
		return default_currency();
	}
	$currencies = get_all_client_currencies();
	if (!is_scalar($id) || !isset($currencies[$id]) || $currencies[$id] === null || $currencies[$id] === '') {
		return default_currency();
	} else {
		return $currencies[$id];
	}
}


/*function client_currency($id)
{
    $CI =& get_instance();
    $CI->load->model('invoice_model');
    $currency_info = $CI->invoice_model->client_currency_symbol($id);
    if (empty($currency_info)) {
        return default_currency();
    } else {
        return $currency_info->symbol;
    }
}*/

// ip_in_range
// This function takes 2 arguments, an IP address and a "range" in several
// different formats.
// Network ranges can be specified as:
// 1. Wildcard format:     1.2.3.*
// 2. CIDR format:         1.2.3/24  OR  1.2.3.4/255.255.255.0
// 3. Start-End IP format: 1.2.3.0-1.2.3.255
// The function will return true if the supplied IP is within the range.
// Note little validation is done on the range inputs - it expects you to
// use one of the above 3 formats.
function iPINRange($ip, $range)
{
	if (strpos($range, '/') !== false) {
		// $range is in IP/NETMASK format
		list($range, $netmask) = explode('/', $range, 2);
		if (strpos($netmask, '.') !== false) {
			// $netmask is a 255.255.0.0 format
			$netmask = str_replace('*', '0', $netmask);
			$netmask_dec = ip2long($netmask);

			return ((ip2long($ip) & $netmask_dec) == (ip2long($range) & $netmask_dec));
		} else {
			// $netmask is a CIDR size block
			// fix the range argument
			$x = explode('.', $range);
			while (count(array($x)) < 4) {
				$x[] = '0';
			}
			list($a, $b, $c, $d) = $x;
			$range = sprintf("%u.%u.%u.%u", empty($a) ? '0' : $a, empty($b) ? '0' : $b, empty($c) ? '0' : $c, empty($d) ? '0' : $d);
			$range_dec = ip2long($range);
			$ip_dec = ip2long($ip);

			# Strategy 1 - Create the netmask with 'netmask' 1s and then fill it to 32 with 0s
			#$netmask_dec = bindec(str_pad('', $netmask, '1') . str_pad('', 32-$netmask, '0'));

			# Strategy 2 - Use math to create it
			$wildcard_dec = pow(2, (32 - $netmask)) - 1;
			$netmask_dec = ~$wildcard_dec;

			return (($ip_dec & $netmask_dec) == ($range_dec & $netmask_dec));
		}
	} else {
		// range might be 255.255.*.* or 1.2.3.0-1.2.3.255
		if (strpos($range, '*') !== false) { // a.b.*.* format
			// Just convert to A-B format by setting * to 0 for A and 255 for B
			$lower = str_replace('*', '0', $range);
			$upper = str_replace('*', '255', $range);
			$range = "$lower-$upper";
		}

		if (strpos($range, '-') !== false) { // A-B format
			list($lower, $upper) = explode('-', $range, 2);
			$lower_dec = (float)sprintf("%u", ip2long($lower));
			$upper_dec = (float)sprintf("%u", ip2long($upper));
			$ip_dec = (float)sprintf("%u", ip2long($ip));

			return (($ip_dec >= $lower_dec) && ($ip_dec <= $upper_dec));
		}
		echo 'Range argument is not in 1.2.3.4/24 or 1.2.3.4/255.255.255.0 format';
		return false;
	}
}

/**
 * Generate md5 hash
 * @return string
 */
function app_generate_hash()
{
	return md5(rand() . microtime() . time() . uniqid());
}

function send_later($params)
{
	$emails = array(
		'sent_to' => $params['recipient'],
		// 'sent_cc' => $params['cc'],
		'sent_from' => config_item('company_email') . ' ' . config_item('company_name'),
		'subject' => $params['subject'],
		'message' => $params['message']
	);
	$CI = &get_instance();
	$CI->db->insert('tbl_outgoing_emails', $emails);
	return TRUE;
}

/**
 * Return server temporary directory
 * @return string
 */
function get_temp_dir()
{
	$temp = TEMP_FOLDER;
	if (is_dir($temp) && is_writable($temp)) {
		return $temp;
	}

	if (function_exists('sys_get_temp_dir')) {
		$temp = sys_get_temp_dir();
		if (@is_dir($temp) && is_writable($temp)) {
			return rtrim($temp, '/\\') . '/';
		}
	}

	$temp = ini_get('upload_tmp_dir');
	if (@is_dir($temp) && is_writable($temp)) {
		return rtrim($temp, '/\\') . '/';
	}


	return '/tmp/';
}


/**
 * Function that strip all html tags from string/text/html
 * @param string $str
 * @param string $allowed prevent specific tags to be stripped
 * @return string
 */
function strip_html_tags($str, $allowed = '')
{
	if (!empty($allowed) && $allowed == 1) {
		$allowed = "<p>,<br>,<strong>";
	}
	$str = preg_replace('/^(?=.*\d)(?=.*[-_+=*&]).{3,}$/', '', $str);
	$str = preg_replace(array(
		// Remove invisible content
		'@<head[^>]*?>.*?</head>@siu',
		'@<style[^>]*?>.*?</style>@siu',
		'@<script[^>]*?.*?</script>@siu',
		'@<object[^>]*?.*?</object>@siu',
		'@<embed[^>]*?.*?</embed>@siu',
		'@<applet[^>]*?.*?</applet>@siu',
		'@<noframes[^>]*?.*?</noframes>@siu',
		'@<noscript[^>]*?.*?</noscript>@siu',
		'@<noembed[^>]*?.*?</noembed>@siu',
		// Add line breaks before and after blocks
		'@</?((address)|(blockquote)|(center)|(del))@iu',
		'@</?((div)|(h[1-9])|(ins)|(isindex)|(p)|(pre))@iu',
		'@</?((dir)|(dl)|(dt)|(dd)|(li)|(menu)|(ol)|(ul))@iu',
		'@</?((table)|(th)|(td)|(caption))@iu',
		'@</?((form)|(button)|(fieldset)|(legend)|(input))@iu',
		'@</?((label)|(select)|(optgroup)|(option)|(textarea))@iu',
		'@</?((frameset)|(frame)|(iframe))@iu'
	), array(
		' ',
		' ',
		' ',
		' ',
		' ',
		' ',
		' ',
		' ',
		' ',
		"\n\$0",
		"\n\$0",
		"\n\$0",
		"\n\$0",
		"\n\$0",
		"\n\$0",
		"\n\$0",
		"\n\$0"
	), $str);

	$str = strip_tags($str, $allowed);

	// Remove on events from attributes
	$re = '/\bon[a-z]+\s*=\s*(?:([\'"]).+?\1|(?:\S+?\(.*?\)(?=[\s>])))/i';
	$str = preg_replace($re, '', $str);

	return $str;
}

function update_config($name, $value)
{
	$CI = &get_instance();
	$CI->db->where('config_key', $name);
	$data = [
		'value' => $value,
	];
	$CI->db->update('tbl_config', $data);
	if ($CI->db->affected_rows() > 0) {
		return true;
	}
	return false;
}

function activity_log($data)
{
	$CI = &get_instance();
	$activity = array(
		'user' => $CI->session->userdata('user_id'),
		'module' => (!empty($data['module']) ? $data['module'] : '-'),
		'module_field_id' => (!empty($data['id']) ? $data['id'] : ''),
		'activity' => (!empty($data['activity']) ? $data['activity'] : '-'),
		'icon' => (!empty($data['icon']) ? $data['icon'] : ''),
		'link' => (!empty($data['url']) ? $data['url'] : ''),
		'value1' => (!empty($data['value1']) ? $data['value1'] : ''),
		'value2' => (!empty($data['value2']) ? $data['value2'] : '')
	);
	$CI->db->insert('tbl_activities', $activity);
}

/**
 * String starts with
 * @param string $haystack
 * @param string $needle
 * @return boolean
 */
	if (!function_exists('_startsWith')) {
		function _startsWith($haystack, $needle)
		{
			// search backwards starting from haystack length characters from the end
			$haystack = (string) $haystack;
			$needle = (string) $needle;
			return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== false;
		}
	}
/**
 * String ends with
 * @param string $haystack
 * @param string $needle
 * @return boolean
 */
	if (!function_exists('endsWith')) {
		function endsWith($haystack, $needle)
		{
			// search forward starting from end minus needle length characters
			$haystack = (string) $haystack;
			$needle = (string) $needle;
			return $needle === "" || (($temp = strlen($haystack) - strlen($needle)) >= 0 && strpos($haystack, $needle, $temp) !== false);
		}
	}
/**
 * Check if there is html in string
 */
if (!function_exists('is_html')) {
	function is_html($string)
	{
		return preg_match("/<[^<]+>/", $string, $m) != 0;
	}
}
function admin_head($user_id = null)
{
	if (!empty(admin($user_id))) {
		return true;
	} elseif (!empty(is_department_head($user_id))) {
		return true;
	} else {
		return false;
	}
}

function custom_form_table_search($id)
{

	$CI = &get_instance();
	$CI->load->model('admin_model');
	$cacheKey = 'custom_form_table_search_' . $id . '_' . (client() ? 'client' : 'staff');
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}

	$all_field = $CI->db->where(array('form_id' => $id, 'status' => 'active', 'show_on_table' => 'on'))->get('tbl_custom_field')->result();
	$form = $CI->db->where('form_id', $id)->get('tbl_form')->row();
	$table = $form->tbl_name;
	$filed_id = $form->table_id;
	$showValue = array();
	if (!empty($all_field)) {
		foreach ($all_field as $v_fileds) {
			if (!empty($v_fileds->visible_for_admin) && empty(client())) {
				if (!empty(admin())) {
					array_push($showValue, $table . '.' . slug_it($v_fileds->field_label));
				}
			} else if (!empty($v_fileds->visible_for_client) && !empty(client())) {
				array_push($showValue, $table . '.' . slug_it($v_fileds->field_label));
			} else if (empty(client())) {
				array_push($showValue, $table . '.' . slug_it($v_fileds->field_label));
			}
		}
	}
	helper_cache_save($cacheKey, $showValue, 900);
	return $showValue;
}

function attendance_access()
{
	$CI = &get_instance();
	$IP = $CI->input->ip_address();
	$only_allowed_ip_can_clock = config_item('only_allowed_ip_can_clock');
	if ($only_allowed_ip_can_clock == 'TRUE') {
		$IP_info = $CI->db->where(array('allowed_ip' => $IP))->get('tbl_allowed_ip')->row();
		if (!empty($IP_info)) {
			if ($IP_info->status == 'active') {
				return true;
			}
		} else {

			$CI->load->model('settings_model');
			$CI->settings_model->_table_name = 'tbl_allowed_ip';
			$CI->settings_model->_primary_key = 'allowed_ip_id';
			// input data
			$cate_data['allowed_ip'] = $IP;
			$cate_data['status'] = 'pending';
			$allowed_ip_id = null;

			// check check_allowed_ip by where
			// if not empty show alert message else save data
			$check_allowed_ip = $CI->settings_model->check_update('tbl_allowed_ip', $where = array('allowed_ip' => $cate_data['allowed_ip']), $allowed_ip_id);
			if (empty($check_allowed_ip)) { // if input data already exist show error alert
				$id = $CI->settings_model->save($cate_data);

				send_clock_email('trying_clock_email');

				$activity = array(
					'user' => $CI->session->userdata('user_id'),
					'module' => 'settings',
					'module_field_id' => $id,
					'activity' => ('activity_added_a_allowed_ip'),
					'value1' => $cate_data['allowed_ip']
				);
				$CI->settings_model->_table_name = 'tbl_activities';
				$CI->settings_model->_primary_key = 'activities_id';
				$CI->settings_model->save($activity);
			}
			return false;
		}
	} else {
		return true;
	}
}

function send_clock_email($type)
{
	if (!empty(config_item('send_clock_email'))) {
		$CI = &get_instance();
		$IP = $CI->input->ip_address();
		$CI->load->model('settings_model');
		$email_template = email_templates(array('email_group' => $type), my_id(), true);
		$message = $email_template->template_body;
		$staff_info = profile();
		$subject = str_replace("{NAME}", $staff_info->fullname . '(' . $staff_info->employment_id . ')', $email_template->subject);
		$name = str_replace("{NAME}", $staff_info->fullname . '(' . $staff_info->employment_id . ')', $message);
		$allowed_ip = str_replace("{IP}", $IP, $name);
		$time = str_replace("{TIME}", display_datetime(date('Y-m-d H:i:s')), $allowed_ip);
		if ($type == 'trying_clock_email') {
			$url = 'admin/attendance/time_history';
		} else {
			$url = 'admin/attendance/time_history';
		}
		$site_url = str_replace("{URL}", base_url($url), $time);
		$message = str_replace("{SITE_NAME}", config_item('company_name'), $site_url);

		$params['subject'] = '[ ' . config_item('company_name') . ' ]' . ' ' . $subject;
		$params['message'] = $message;
		$params['resourceed_file'] = '';
		$all_admin = all_admin();
		if (!empty($all_admin)) {
			foreach ($all_admin as $v_user) {
				$params['recipient'] = $v_user->email;
				$CI->settings_model->send_email($params);
			}
		}
		return true;
	}
	return true;
}

function number_to_word($client_id = null, $amount = null)
{
	$ci = &get_instance();
	if (!empty($client_id)) {
		if (is_numeric($client_id)) {
			$client_id = ['client_id' => $client_id];
		} else {
			$client_id = ['currency' => $client_id];
		}
	} else {
		$client_id = '';
	}
	$ci->load->library('number_to_word', $client_id, 'numberword');
	return $ci->numberword->convert($amount);
}

function twilio_send_sms($number, $message)
{

	// Using composer
	require_once(APPPATH . '/third_party/twilio/Twilio/autoload.php');

	// Account SID from twilio.com/console
	static $sid;
	// Auth Token from twilio.com/console
	static $token;
	// Twilio Phone Number
	static $mobile;

	if (!$sid) {
		$sid = config_item('twilio_account_sid');
	}

	if (!$token) {
		$token = config_item('twilio_auth_token');;
	}

	if (!$mobile) {
		$mobile = config_item('twilio_phone_number');
	}
	try {
		$client = new Twilio\Rest\Client($sid, $token);
	} catch (Exception $e) {
		$GLOBALS['sms_error'] = $e->getMessage();
		return false;
	}

	try {
		$client->messages->create(
			// The number to send the SMS
			$number,
			[
				// A Twilio phone number you purchased at twilio.com/console
				'from' => $mobile,
				'body' => $message,
			]
		);
	} catch (Exception $e) {
		$error = $e->getMessage();
		$GLOBALS['sms_error'] = $error;
		return false;
	}

	return true;
}


function clickatell_send_sms($number, $message)
{
	static $api_key;

	if (!$api_key) {
		$api_key = config_item('clickatell_api_key');
	}
	// No from number, in clickatell from is used only in 2 way messaging
	$url = 'https://platform.clickatell.com/messages/http/send?' . http_build_query([
		'apiKey' => $api_key,
		'to' => $number,
		'content' => $message,
	]);

	$ch = curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
	curl_setopt($ch, CURLOPT_HTTPHEADER, [
		'X-Version: 1',
		'Content-Type: application/json',
		'Accept: application/json',
	]);

	$result = curl_exec($ch);

	$error = false;

	if (!$result) {
		$error = curl_error($ch);
	} elseif ($result) {
		$result = json_decode($result);

		if (isset($result->messages[0]->accepted) && $result->messages[0]->accepted == true) {
			//            logActivity('SMS to send via Clickatell to ' . $number . ', Message: ' . $message);

			return true;
		} elseif (isset($result->messages) && isset($result->error)) {
			$error = $result->error;
		} elseif (isset($result->messages[0]->error) && $result->messages[0]->error != null) {
			$error = $result->messages[0]->error;
		}
	}

	if ($error !== false && $error !== null) {
		$GLOBALS['sms_error'] = $error;
	}

	return false;
}


function can_send_sms_by_created_date($data_date_created)
{
	$now = time();
	$your_date = safe_strtotime($data_date_created);
	$datediff = $now - $your_date;

	$days_diff = floor($datediff / (60 * 60 * 24));
	return $days_diff < 45 || $days_diff == 45;
}

function can_received_sms($type)
{
	$number = config_item($type);
	if (!empty($number) && is_numeric($number)) {
		return $number;
	}
	return false;
}

function client_can_received_sms($client_id)
{
	$client = get_row('tbl_client', array('client_id' => $client_id));
	if (!empty($client) && $client->sms_notification == 1) {
		$user_info = get_staff_details($client->primary_contact);
		if (!empty($user_info->mobile)) {
			return $user_info->mobile;
		} elseif ($client->mobile) {
			return $client->mobile;
		}
	}
	return false;
}


function merge_transactions_template($type, $transaction_id)
{
	$fields = [];
	if (!empty($transaction_id)) {
		$transaction_info = get_row('tbl_transactions', array('transactions_id' => $transaction_id));
		if (!$transaction_info) {
			return $fields;
		}
		$client_name = '';
		$accounts_info = get_row('tbl_accounts', array('account_id' => $transaction_info->account_id));
		if (!empty($transaction_info->paid_by)) {
			$client = get_row('tbl_client', array('client_id' => $transaction_info->paid_by));
			if (!empty($client)) {
				$client_name = $client->name;
			}
		}
		$fields['{transaction_paid_by}'] = $client_name;
		if (!empty($accounts_info)) {
			$account_name = $accounts_info->account_name;
		} else {
			$account_name = '';
		}
		$fields['{transaction_type}'] = lang($type);
		$fields['{transaction_title}'] = $transaction_info->name;
		$fields['{transaction_link}'] = base_url('admin/transactions/view_details/' . $transaction_id);
		$fields['{transaction_date}'] = display_date($transaction_info->date);
		$fields['{transaction_amount}'] = display_money($transaction_info->amount);
		$fields['{transaction_account}'] = $account_name;
		$fields['{transaction_balance}'] = display_money($transaction_info->total_balance, default_currency());
		$fields['{site_name}'] = config_item('company_name');
	}
	return $fields;
}

function merge_purchase_template($purchase_id = null, $payment_id = null)
{
	$fields = [];
	if (!empty($purchase_id)) {
		$purchase_info = get_row('tbl_purchases', array('purchase_id' => $purchase_id));
		if (!$purchase_info) {
			return $fields;
		}
		$CI = &get_instance();
		$CI->load->model('purchase_model');
		$payment_status = $CI->purchase_model->get_payment_status($purchase_info->purchase_id);
		$supplier_info = get_row('tbl_suppliers', array('supplier_id' => $purchase_info->supplier_id));
		if (!empty($supplier_info)) {
			$client_name = $supplier_info->name;
			$email = $supplier_info->email;
		} else {
			$client_name = '-';
			$email = '-';
		}
		$fields['{supplier_name}'] = $client_name;
		$fields['{supplier_email}'] = $email;
		$fields['{purchase_link}'] = base_url('admin/purchase/purchase_details/' . $purchase_id);
		$fields['{purchase_ref}'] = $purchase_info->reference_no;
		$fields['{purchase_date}'] = display_date($purchase_info->purchase_date);
		$fields['{purchase_due_date}'] = display_date($purchase_info->due_date);
		$fields['{purchase_status}'] = lang($payment_status);
		$fields['{purchase_subtotal}'] = display_money($CI->purchase_model->calculate_to('purchase_cost', $purchase_id));
		$fields['{purchase_total}'] = display_money($CI->purchase_model->calculate_to('total', $purchase_id), default_currency());
		$fields['{site_name}'] = config_item('company_name');
		if (!empty($payment_id)) {
			$payments_info = get_row('tbl_purchase_payments', array('payments_id' => $payment_id));
			$fields['{payment_amount}'] = display_money($payments_info->amount, $payments_info->currency);
			$fields['{payment_date}'] = display_date($payments_info->payment_date);
		}
	}
	return $fields;
}

function merge_return_stock_template($return_stock_id = null, $payment_id = null)
{
	$fields = [];
	if (!empty($return_stock_id)) {
		$return_stock_info = get_row('tbl_return_stock', array('return_stock_id' => $return_stock_id));
		if (!$return_stock_info) {
			return $fields;
		}
		$CI = &get_instance();
		$CI->load->model('return_stock_model');
		if ($return_stock_info->module == 'client') {
			$client_info = $CI->return_stock_model->check_by(array('client_id' => $return_stock_info->module_id), 'tbl_client');
			$fields['{full_name}'] = fullname($client_info->primary_contact);
			$fields['{client_name}'] = $client_info->name;
			$fields['{contact_email}'] = $client_info->email;
		} else if ($return_stock_info->module == 'supplier') {
			$client_info = $CI->return_stock_model->check_by(array('supplier_id' => $return_stock_info->module_id), 'tbl_suppliers');
			$fields['{supplier_name}'] = $client_info->name;
			$fields['{supplier_email}'] = $client_info->email;
		}
		$fields['{return_stock_link}'] = base_url('admin/return_stock/return_stock_details/' . $return_stock_id);
		$fields['{return_stock_ref}'] = $return_stock_info->reference_no;
		$fields['{return_stock_date}'] = display_date($return_stock_info->return_stock_date);
		$fields['{return_stock_due_date}'] = display_date($return_stock_info->due_date);
		$fields['{return_stock_status}'] = lang($return_stock_info->status);
		$fields['{return_stock_subtotal}'] = display_money($CI->return_stock_model->calculate_to('return_stock_cost', $return_stock_id));
		$fields['{return_stock_total}'] = display_money($CI->return_stock_model->calculate_to('total', $return_stock_id), default_currency());
		$fields['{site_name}'] = config_item('company_name');
		if (!empty($payment_id)) {
			$payments_info = get_row('tbl_return_stock_payments', array('payments_id' => $payment_id));
			$fields['{payment_amount}'] = display_money($payments_info->amount, $payments_info->currency);
			$fields['{payment_date}'] = display_date($payments_info->payment_date);
		}
	}
	return $fields;
}

function merge_reminder_template($reminder_id = null, $name = null, $related_link = null)
{
	$fields = [];
	if (!empty($reminder_id)) {
		$reminder_info = get_row('tbl_reminders', array('reminder_id' => $reminder_id));
		if (!$reminder_info) {
			return $fields;
		}
		$fields['{name}'] = $name;
		$fields['{reminder_description}'] = $reminder_info->description;
		$fields['{reminder_date}'] = display_date($reminder_info->date);
		$fields['{reminder_related}'] = $reminder_info->module;
		$fields['{reminder_related_link}'] = base_url($related_link);
		$fields['{site_name}'] = config_item('company_name');
	}
	return $fields;
}


function merge_proposal_template($proposals_id = null)
{
	$fields = [];
	$CI = &get_instance();
	if (!empty($proposals_id)) {
		$inv_info = get_row('tbl_proposals', array('proposals_id' => $proposals_id));
		if (!$inv_info) {
			return $fields;
		}
		$CI->load->model('proposal_model');
		$client_info = $CI->proposal_model->check_by(array('client_id' => $inv_info->module_id), 'tbl_client');
		if (!empty($client_info)) {
			$currency = $CI->proposal_model->client_currency_symbol($client_info->client_id);
		} else {
			$currency = $CI->proposal_model->check_by(array('code' => config_item('default_currency')), 'tbl_currencies');
		}
		$currency = $currency->symbol;
		$fields['{proposal_link}'] = base_url('client/proposals/index/proposals_details/' . $proposals_id);
		$fields['{proposal_ref}'] = $inv_info->reference_no;
		$fields['{proposal_date}'] = display_date($inv_info->proposal_date);
		$fields['{proposal_due_date}'] = display_date($inv_info->due_date);
		$fields['{proposal_status}'] = lang($inv_info->status);
		$fields['{proposal_subtotal}'] = display_money($CI->proposal_model->proposal_calculation('proposal_cost', $inv_info->proposals_id));
		$fields['{proposal_total}'] = display_money($CI->proposal_model->proposal_calculation('total', $inv_info->proposals_id), $currency);
		$fields['{proposal_related_to}'] = $inv_info->module;
		$fields['{site_name}'] = config_item('company_name');
	}
	return $fields;
}

function merge_estimate_template($estimates_id = null)
{
	$fields = [];
	$CI = &get_instance();
	if (!empty($estimates_id)) {
		$inv_info = get_row('tbl_estimates', array('estimates_id' => $estimates_id));
		if (!$inv_info) {
			return $fields;
		}
		$CI->load->model('estimates_model');
		$client_info = $CI->estimates_model->check_by(array('client_id' => $inv_info->client_id), 'tbl_client');
		if (!empty($client_info)) {
			$currency = $CI->estimates_model->client_currency_symbol($client_info->client_id);
		} else {
			$currency = $CI->estimates_model->check_by(array('code' => config_item('default_currency')), 'tbl_currencies');
		}
		$currency = $currency->symbol;
		$fields['{estimate_link}'] = base_url('client/estimates/index/estimates_details/' . $estimates_id);
		$fields['{estimate_ref}'] = $inv_info->reference_no;
		$fields['{estimate_date}'] = display_date($inv_info->estimate_date);
		$fields['{estimate_due_date}'] = display_date($inv_info->due_date);
		$fields['{estimate_status}'] = lang($inv_info->status);
		$fields['{estimate_subtotal}'] = display_money($CI->estimates_model->estimate_calculation('estimate_amount', $inv_info->estimates_id));
		$fields['{estimate_total}'] = display_money($CI->estimates_model->estimate_calculation('total', $inv_info->estimates_id), $currency);
		$fields['{site_name}'] = config_item('company_name');
	}
	return $fields;
}

function merge_invoice_template($invoices_id = null, $type = null, $type_id = null)
{
	$fields = [];
	$CI = &get_instance();
	if (!empty($invoices_id)) {
		$inv_info = get_row('tbl_invoices', array('invoices_id' => $invoices_id));
		if (!$inv_info) {
			return $fields;
		}
		$CI->load->model('invoice_model');
		$client_info = $CI->invoice_model->check_by(array('client_id' => $inv_info->client_id), 'tbl_client');
		if (!empty($client_info)) {
			$currency = $CI->invoice_model->client_currency_symbol($client_info->client_id);
		} else {
			$currency = $CI->invoice_model->check_by(array('code' => config_item('default_currency')), 'tbl_currencies');
		}
		$currency = $currency->symbol;
		$payment_status = $CI->invoice_model->get_payment_status($invoices_id);
		$fields['{invoice_link}'] = base_url('client/invoice/manage_invoice/invoice_details/' . $invoices_id);
		$fields['{invoice_ref}'] = $inv_info->reference_no;
		$fields['{invoice_date}'] = display_date($inv_info->invoice_date);
		$fields['{invoice_due_date}'] = display_date($inv_info->due_date);
		$fields['{invoice_status}'] = lang($payment_status);
		$fields['{invoice_subtotal}'] = display_money($CI->invoice_model->calculate_to('invoice_cost', $invoices_id));
		$fields['{invoice_total}'] = display_money($CI->invoice_model->calculate_to('total', $invoices_id), $currency);
		$fields['{site_name}'] = config_item('company_name');
	}
	if (!empty($type) && $type == 'payment') {
		$payments_info = get_row('tbl_payments', array('payments_id' => $type_id));
		$fields['{payment_amount}'] = display_money($payments_info->amount, $currency);
		$fields['{payment_date}'] = display_date($payments_info->payment_date);
	}
	if (!empty($type) && $type == 'client') {
		$client = get_row('tbl_client', array('client_id' => $type_id));
		if (!empty($client)) {
			$fields['{full_name}'] = fullname($client->primary_contact);
			$fields['{client_name}'] = client_name($type_id);
			$fields['{contact_email}'] = client_contact_email($type_id);
		}
	}
	return $fields;
}

function _parse_template_merge_fields($template, $merge_fields)
{
	foreach ($merge_fields as $key => $val) {
		if (stripos($template->message, $key) !== false) {
			$template->message = str_ireplace($key, $val, $template->message);
		} else {
			$template->message = str_ireplace($key, '', $template->message);
		}
		if (stripos($template->subject, $key) !== false) {
			$template->subject = str_ireplace($key, $val, $template->subject);
		} else {
			$template->subject = str_ireplace($key, '', $template->subject);
		}
	}
	return $template;
}

function my_department_head($user_id = null)
{
	$CI = &get_instance();
	if (empty($user_id)) {
		$user_id = my_id();
	}
	$cacheKey = 'my_department_head_' . $user_id;
	$cached = helper_cache_get($cacheKey);
	if ($cached !== false) {
		return $cached;
	}
	$staff_details = MyDetails($user_id);
	if (!empty($staff_details->departments_id)) {
		$department = $CI->db->where('departments_id', $staff_details->departments_id)->get('tbl_departments')->row();
		$result = $department->department_head_id;
	} else {
		$result = false;
	}
	helper_cache_save($cacheKey, $result, 900);
	return $result;
}

function language_code($id = null, $user = null)
{
	if (!empty($id)) {
		if (!empty($user)) {
			$language = get_any_field('tbl_account_details', array('user_id' => $id), 'language');
		} else {
			$language = get_any_field('tbl_client', array('client_id' => $id), 'language');
		}
	}
	if (empty($language)) {
		$language = config_item('default_language');
	}
	$result = get_any_field('tbl_languages', array('name' => $language), 'code');
	if (empty($result)) {
		$result = 'en';
	}
	return $result;
}

function email_templates($where, $id = null, $user = null)
{
	$where['code'] = language_code($id, $user);
	$result = get_row('tbl_email_templates', $where);
	return $result;
}

function available_tags()
{
	$all_tags = get_result('tbl_tags');
	$tag_names = [];
	foreach ($all_tags as $tag) {
		$tag_name = !empty($tag->name) ? trim($tag->name) : '';
		if ($tag_name !== '' && !in_array($tag_name, $tag_names, true)) {
			array_push($tag_names, $tag_name);
		}
	}
	return $tag_names;
}

function normalize_tag_list($tags)
{
	$tag_names = array();
	foreach (explode(',', (string) $tags) as $tag_name) {
		$tag_name = trim($tag_name);
		if ($tag_name !== '' && !in_array($tag_name, $tag_names, true)) {
			$tag_names[] = $tag_name;
		}
	}
	return implode(', ', $tag_names);
}

function normalize_and_sync_tags($tags)
{
	$tags = normalize_tag_list($tags);
	if ($tags !== '') {
		update_module_tags($tags);
	}
	return $tags;
}

function tag_input_attrs(array $attrs = array())
{
	unset($attrs['data-role']);
	$classes = !empty($attrs['class']) ? preg_split('/\s+/', trim($attrs['class'])) : array();
	if (!in_array('tw-tags', $classes, true)) {
		$classes[] = 'tw-tags';
	}
	$attrs['class'] = trim(implode(' ', array_filter($classes)));
	if (empty($attrs['placeholder'])) {
		$attrs['placeholder'] = lang('add_tags');
	}
	if (empty($attrs['data-available-tags'])) {
		$attrs['data-available-tags'] = json_encode(array_values(available_tags()), JSON_HEX_APOS | JSON_HEX_QUOT);
	}
	return $attrs;
}

function form_tags_group($value = '', array $attrs = array(), $label = null, $name = 'tags', $required = false, $layout = 'stacked')
{
	return form_input_group($name, $label ?: lang('tags'), $value, 'text', $required, tag_input_attrs($attrs), '', '', $layout);
}

function form_tags_input($value = '', array $attrs = array(), $name = 'tags')
{
	$attrs = tag_input_attrs($attrs);
	$attrs['id'] = !empty($attrs['id']) ? $attrs['id'] : $name;
	$attrs['name'] = $name;
	$attrs['type'] = 'text';
	return '<input ' . build_html_attributes($attrs) . ' value="' . html_escape($value) . '">';
}

function get_tags($tags, $view = null)
{
	if (empty($tags)) {
		return '';
	}
	$all_tags = explode(',', $tags);
	if (!empty($all_tags)) {
		$tag_names = [];
		foreach ($all_tags as $tag) {
			$tag = trim($tag);
			if ($tag !== '') {
				$tag_row = get_row('tbl_tags', array('name' => $tag));
				if (!empty($tag_row)) {
					array_push($tag_names, $tag_row->name);
					if (!empty($view)) {
						$result[] = '<span class="label tags" style="' . (!empty($tag_row->style) ? $tag_row->style : '') . '">' . $tag_row->name . '</span>';
					}
				}
			}
		}
		if (!empty($view) && !empty($result)) {
			return implode(' ', $result);
		} else if (!empty($tag_names)) {
			return implode(',', $tag_names);
		}
	}
	return '';
}

function get_html_tags($tags, $view = null)
{
	if (empty($tags)) {
		return '';
	}
	$all_tags = explode(',', $tags);
	if (!empty($all_tags)) {
		$tag_names = [];
		foreach ($all_tags as $tag) {
			if ($tag) {
				$tag_row = get_row('tbl_tags', array('name' => $tag));
				if (!empty($tag_row)) {
					array_push($tag_names, $tag_row->name);
				}
				if (!empty($view)) {
					$result[] = '<span class="label tags" style="' . $tag_row->style . '">' . $tag_row->name . '</span>';
				}
			}
		}
		if (!empty($view) && !empty($result)) {
			return implode(' ', $result);
		} else if (!empty($tag_names)) {
			return implode(',', $tag_names);
		}
	}
	return '';
}

function get_style($style, $string)
{
	$style = explode(';', (string) $style);
	$result = array_filter($style, function ($value) use ($string) {
		if (strpos((string) $value, (string) $string) !== false) {
			return $value;
		}
		return "";
	});
	return $result[key($result)];
}

function update_module_tags($data)
{
	$CI = &get_instance();

	$normalized_tags = normalize_tag_list($data);
	$input_tags = $normalized_tags !== '' ? explode(', ', $normalized_tags) : array();
	$available_tags = available_tags();
	if (is_array($input_tags)) {
		$get_tags = array_diff($input_tags, $available_tags);
		if (!empty($get_tags)) {
			foreach ($get_tags as $tag) {
				$d_tag[] = array(
					'name' => $tag,
					'style' => '',
				);
			}
			if (!empty($d_tag) && is_array($d_tag)) {
				$CI->db->insert_batch('tbl_tags', $d_tag);
			}
		}
	}
	return true;
}

function already_tags($table, $id = null)
{
	$tags = get_row($table, array('project_id' => $id))->tags;
	if (!empty($tags)) {
		return explode(',', $tags);
	}
	return array();
}

if (!function_exists('render_form_builder_field')) {
	/**
	 * Used for customer forms eq. leads form, builded from the form builder plugin
	 * @param object $field field from database
	 * @return mixed
	 */
	function render_form_builder_field($field)
	{
		$type = $field->type;
		$classNameCol = 'col-md-12';
		if (isset($field->className)) {
			$fieldClassName = (string) $field->className;
			if (strpos($fieldClassName, 'form-col') !== false) {
				$classNames = explode(' ', $fieldClassName);
				if (is_array($classNames)) {
					$classNameColArray = array_filter($classNames, function ($class) {
						return _startsWith((string) $class, 'form-col');
					});

					$classNameCol = implode(' ', $classNameColArray);
					$classNameCol = trim($classNameCol);

					$classNameCol = str_replace('form-col-xs', 'col-xs', $classNameCol);
					$classNameCol = str_replace('form-col-sm', 'col-sm', $classNameCol);
					$classNameCol = str_replace('form-col-md', 'col-md', $classNameCol);
					$classNameCol = str_replace('form-col-lg', 'col-lg', $classNameCol);

					// Default col-md-X
					$classNameCol = str_replace('form-col', 'col-md', $classNameCol);
				}
			}
		}

		echo '<div class="' . $classNameCol . '">';
		if ($type == 'header' || $type == 'paragraph') {
			echo '<' . $field->subtype . ' class="' . (isset($field->className) ? $field->className : '') . '">' . (nl2br($field->label)) . '</' . $field->subtype . '>';
		} else {
			echo '<div class="form-group" data-type="' . $type . '" data-name="' . $field->name . '" data-required="' . (isset($field->required) ? true : 'false') . '">';
			echo '<label class="control-label" for="' . $field->name . '">' . (isset($field->required) ? ' <span class="text-danger">* </span> ' : '') . $field->label . '' . (isset($field->description) ? ' <i class="circle-question-mark" data-toggle="tooltip" data-title="' . $field->description . '" data-placement="' . 'right' . '"></i>' : '') . '</label>';
			if (isset($field->subtype) && $field->subtype == 'color') {
				echo '<div class="input-group colorpicker-input">
         <input' . (isset($field->required) ? ' required="true"' : '') . ' placeholder="' . (isset($field->placeholder) ? $field->placeholder : '') . '" type="text"' . (isset($field->value) ? ' value="' . $field->value . '"' : '') . ' name="' . $field->name . '" id="' . $field->name . '" class="' . (isset($field->className) ? $field->className : '') . '" />
             <span class="input-group-addon"><i></i></span>
         </div>';
			} elseif ($type == 'file' || $type == 'text' || $type == 'number') {
				$ftype = isset($field->subtype) ? $field->subtype : $type;
				echo '<input' . (isset($field->required) ? ' required="true"' : '') . (isset($field->placeholder) ? ' placeholder="' . $field->placeholder . '"' : '') . ' type="' . $ftype . '" name="' . $field->name . '" id="' . $field->name . '" class="' . (isset($field->className) ? $field->className : '') . '" value="' . (isset($field->value) ? $field->value : '') . '"' . ($field->type == 'file' ? ' accept="' . get_form_accepted_mimes() . '" filesize="' . file_upload_max_size() . '"' : '') . '>';
			} elseif ($type == 'textarea') {
				echo '<textarea' . (isset($field->required) ? ' required="true"' : '') . ' id="' . $field->name . '" name="' . $field->name . '" rows="' . (isset($field->rows) ? $field->rows : '4') . '" class="' . (isset($field->className) ? $field->className : '') . '" placeholder="' . (isset($field->placeholder) ? $field->placeholder : '') . '">' . (isset($field->value) ? $field->value : '') . '</textarea>';
			} elseif ($type == 'date') {
				echo '<input' . (isset($field->required) ? ' required="true"' : '') . ' placeholder="' . (isset($field->placeholder) ? $field->placeholder : '') . '" type="text" class="' . (isset($field->className) ? $field->className : '') . ' datepicker" name="' . $field->name . '" id="' . $field->name . '" value="' . (isset($field->value) ? _d($field->value) : '') . '">';
			} elseif ($type == 'datetime-local') {
				echo '<input' . (isset($field->required) ? ' required="true"' : '') . ' placeholder="' . (isset($field->placeholder) ? $field->placeholder : '') . '" type="text" class="' . (isset($field->className) ? $field->className : '') . ' datetimepicker" name="' . $field->name . '" id="' . $field->name . '" value="' . (isset($field->value) ? _dt($field->value) : '') . '">';
			} elseif ($type == 'select') {
				echo '<select' . (isset($field->required) ? ' required="true"' : '') . '' . (isset($field->multiple) ? ' multiple="true"' : '') . ' class="' . (isset($field->className) ? $field->className : '') . '" name="' . $field->name . (isset($field->multiple) ? '[]' : '') . '" id="' . $field->name . '"' . (isset($field->values) && count(array($field->values)) > 10 ? 'data-live-search="true"' : '') . 'data-none-selected-text="' . (isset($field->placeholder) ? $field->placeholder : '') . '">';
				$values = [];
				if (isset($field->values) && count(array($field->values)) > 0) {
					foreach ($field->values as $option) {
						echo '<option value="' . $option->value . '" ' . (isset($option->selected) ? ' selected' : '') . '>' . $option->label . '</option>';
					}
				}
				echo '</select>';
			} elseif ($type == 'checkbox-group') {
				$values = [];
				if (isset($field->values) && count(array($field->values)) > 0) {
					$i = 0;
					echo '<div class="chk">';
					foreach ($field->values as $checkbox) {
						echo '<div class="checkbox' . ((isset($field->inline) && $field->inline == 'true') || (isset($field->className) && strpos((string) $field->className, 'form-inline-checkbox') !== false) ? ' checkbox-inline' : '') . '">';
						echo '<input' . (isset($field->required) ? ' required="true"' : '') . ' class="' . (isset($field->className) ? $field->className : '') . '" type="checkbox" id="chk_' . $field->name . '_' . $i . '" value="' . $checkbox->value . '" name="' . $field->name . '[]"' . (isset($checkbox->selected) ? ' checked' : '') . '>';
						echo '<label for="chk_' . $field->name . '_' . $i . '">';
						echo $checkbox->label;
						echo '</label>';
						echo '</div>';
						$i++;
					}
					echo '</div>';
				}
			}
			echo '</div>';
		}
		echo '</div>';
	}
}


function module_dirPath($module, $concat = '')
{
	return MODULES_PATH . $module . '/' . $concat;
}

function module_direcoty($module, $concat = '')
{
	return 'modules/' . $module . '/' . $concat;
}

function module_dirURL($module, $segment = '')
{
	return base_url('modules/' . $module . '/' . $segment);
}

function module_languagesFiles($module, $languages = [])
{

	if (is_null($languages) || count(array($languages)) === 0) {
		$languages = [$module];
	}

	foreach ($languages as $language) {
		$CI = &get_instance();

		$path = MODULES_PATH . $module . '/language/' . $language . '/';

		//        foreach ($languages as $file_name) {
		$file_path = $path . $language . '_lang' . '.php';
		if (file_exists($file_path)) {
			$CI->lang->load($module . '/' . $language, $language);
		} elseif ($language != 'english' && !file_exists($file_path)) {
			$CI->lang->load($module . '/' . $language, 'english');
		}
		//        }
	};
}


function leave_days_hours($days)
{
	$office_hours = config_item('office_hours');

	$hours = $days * $office_hours;
	$hours_r = $hours % $office_hours;
	if ($hours_r > 0) {
		$days = (int)($hours / $office_hours);
		$days = $days . ' d ' . $hours_r . ' h ';
	}
	return $days;
}

function password_star($password)
{
	$password = strlen(decrypt($password));
	if (!empty($password)) {
		return str_repeat("*", ($password));
	}
	return false;
}

function get_form_field($field, $data = '')
{
	$value = explode('|', $field);
	$cfields = '';
	if (!empty($value[0])) {
		if ($data != '') {
			$value[0] = $data . '_' . $value[0];
		}
		$field_label = lang($value[0]);
		$config_value = config_item($value[0]);
		if (!empty($value[1])) {
			if ($value[1] == 'password') {
				$password = password_star($config_value);
				$required = empty($password) ? 'required' : '';
				$cfields .= '<div class="rounded-2xl border border-border bg-background/60 p-4">';
				$cfields .= '<div class="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">';
				$cfields .= '<div class="min-w-0 flex-1 space-y-2">';
				$cfields .= '<label class="text-sm font-medium text-foreground">' . $field_label . ' <span class="text-red-500">*</span></label>';
				$cfields .= '<input type="password" name="' . $value[0] . '" autocomplete="new-password" class="w-full rounded-lg border border-border bg-input px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-secondary/30" placeholder="' . (!empty($password) ? $password : $field_label) . '" ' . $required . '>';
				$cfields .= '<p class="text-xs text-muted-foreground">Leave blank to keep the saved secret unchanged.</p>';
				$cfields .= '</div>';
				$cfields .= '<div class="shrink-0">';
				$cfields .= btn_modal(
					'admin/client/see_password/' . $value[0],
					'<i data-lucide="eye" class="h-3.5 w-3.5"></i>' . lang('see_password'),
					lang('see_password'),
					'md',
					'',
					array(
						'id' => 'see_password',
						'class' => 'inline-flex items-center gap-2 rounded-lg border border-border bg-card px-3 py-2 text-sm font-medium text-foreground hover:bg-muted'
					)
				);
				$cfields .= '</div>';
				$cfields .= '</div>';
				$cfields .= '</div>';
			} elseif ($value[1] == 'checkbox') {
				$cfields .= '<div class="rounded-2xl border border-border bg-background/60 p-4">';
				$cfields .= form_checkbox($value[0], $field_label, $config_value == 'TRUE');
				$cfields .= '</div>';
			} elseif ($value[1] == 'textarea') {
				$cfields .= '<div class="rounded-2xl border border-border bg-background/60 p-4">';
				$cfields .= form_textarea_group($value[0], $field_label, $config_value, 4, true);
				$cfields .= '</div>';
			}
		} else {
			$cfields .= '<div class="rounded-2xl border border-border bg-background/60 p-4">';
			$cfields .= form_input_group($value[0], $field_label, $config_value, 'text', true);
			$cfields .= '</div>';
		}
	}


	return $cfields;
}

function is_active_module($module)
{
	$active = get_any_field('tbl_modules', array('module_name' => $module), 'active');
	if (!empty($active) && $active == 1) {
		return true;
	} else {
		set_message('error', 'you need to install/active the ' . $module . '  module to run');
		if (empty($_SERVER['HTTP_REFERER'])) {
			redirect('login');
		} else {
			redirect($_SERVER['HTTP_REFERER']);
		}
	}
}

function short_text($string = FALSE, $from_start = 16, $from_end = 8, $limit = FALSE)
{
	if (!$string) {
		return FALSE;
	}
	if (mb_strlen($string) < $from_start) {
		return $string;
	}
	return mb_substr($string, 0, $from_start - 1) . "..." . ($from_end > 0 ? mb_substr($string, -$from_end) : '');
}

// function vincentyGreatCircleDistance($latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
// {
//     // convert from degrees to radians
//     $latFrom = deg2rad($latitudeFrom);
//     $lonFrom = deg2rad($longitudeFrom);
//     $latTo = deg2rad($latitudeTo);
//     $lonTo = deg2rad($longitudeTo);

//     $lonDelta = $lonTo - $lonFrom;
//     $a = pow(cos($latTo) * sin($lonDelta), 2) +
//         pow(cos($latFrom) * sin($latTo) - sin($latFrom) * cos($latTo) * cos($lonDelta), 2);
//     $b = sin($latFrom) * sin($latTo) + cos($latFrom) * cos($latTo) * cos($lonDelta);

//     $angle = atan2(sqrt($a), $b);
//     return $angle * $earthRadius;
// }
function vincentyGreatCircleDistance($latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
{
	// convert from degrees to radians
	$latFrom = deg2rad($latitudeFrom);
	$lonFrom = deg2rad($longitudeFrom);
	$latTo = deg2rad($latitudeTo);
	$lonTo = deg2rad($longitudeTo);

	$lonDelta = $lonTo - $lonFrom;
	$a = pow(cos($latTo) * sin($lonDelta), 2) + pow(cos($latFrom) * sin($latTo) - sin($latFrom) * cos($latTo) * cos($lonDelta), 2);
	$b = sin($latFrom) * sin($latTo) + cos($latFrom) * cos($latTo) * cos($lonDelta);

	$angle = atan2(sqrt($a), $b);
	// distance / 1000 = distance in km
	return ($angle * $earthRadius) / 1000;
}

function bugs_details_tabs($id)
{
	$url = 'admin/bugs/details/';

	$tabs = array(
		'details' => [
			'position' => 1,
			'name' => 'details',
			'url' => $url . $id,
			'count' => '',
			'view' => $url . 'index'
		],
		'comments' => [
			'position' => 2,
			'name' => 'comments',
			'url' => $url . $id . '/comments',
			'count' => total_rows('tbl_task_comment', array('module' => 'bugs', 'module_field_id' => $id)),
			'view' => 'admin/common/comments'
		],
		'attachments' => [
			'position' => 3,
			'name' => 'attachments',
			'url' => $url . $id . '/attachments',
			'count' => total_rows('tbl_attachments', array('module' => 'bugs', 'module_field_id' => $id)),
			'view' => 'admin/common/attachments'
		],
		'tasks' => [
			'position' => 4,
			'name' => 'tasks',
			'url' => $url . $id . '/tasks',
			'count' => total_rows('tbl_task', array('bug_id' => $id)),
			'view' => $url . 'tasks'
		],
		'notes' => [
			'position' => 5,
			'name' => 'notes',
			'url' => $url . $id . '/notes',
			'count' => '',
			'view' => 'admin/common/notes_log'
		],
		'activities' => [
			'position' => 6,
			'name' => 'activities',
			'url' => $url . $id . '/activities',
			'count' => total_rows('tbl_activities', array('module' => 'bugs', 'module_field_id' => $id)),
			'view' => 'admin/common/activites'
		],


	);
	return apply_filters('bugs_details_tabs', $tabs);
}

function opportunities_details_tabs($id)
{
	$url = 'admin/opportunities/opportunity_details/';

	$tabs = array(
		'details' => [
			'position' => 1,
			'name' => 'details',
			'url' => $url . $id,
			'count' => '',
			'view' => $url . 'index'
		],
		'call' => [
			'position' => 2,
			'name' => 'call',
			'url' => $url . $id . '/call',
			'count' => total_rows('tbl_calls', array('opportunities_id' => $id)),
			'view' => 'admin/common/call_log'
		],
		'mettings' => array(
			'position' => 3,
			'name' => 'mettings',
			'url' => $url . $id . '/mettings',
			'count' => total_rows('tbl_mettings', array('opportunities_id' => $id)),
			'view' => 'admin/common/meeting_log',
		),
		'comments' => [
			'position' => 4,
			'name' => 'comments',
			'url' => $url . $id . '/comments',
			'count' => total_rows('tbl_task_comment', array('module' => 'opportunities', 'module_field_id' => $id)),
			'view' => 'admin/common/comments'
		],
		'attachments' => [
			'position' => 5,
			'name' => 'attachments',
			'url' => $url . $id . '/attachments',
			'count' => total_rows('tbl_attachments', array('module' => 'opportunities', 'module_field_id' => $id)),
			'view' => 'admin/common/attachments'
		],
		'tasks' => [
			'position' => 6,
			'name' => 'tasks',
			'url' => $url . $id . '/tasks',
			'count' => total_rows('tbl_task', array('opportunities_id' => $id)),
			'view' => $url . 'task'
		],
		'bugs' => [
			'position' => 7,
			'name' => 'bugs',
			'url' => $url . $id . '/bugs',
			'count' => total_rows('tbl_bug', array('opportunities_id' => $id)),
			'view' => $url . 'bugs'
		],
		'activities' => [
			'position' => 8,
			'name' => 'activities',
			'url' => $url . $id . '/activities',
			'count' => total_rows('tbl_activities', array('module' => 'opportunities', 'module_field_id' => $id)),
			'view' => 'admin/common/activites'
		],


	);
	return apply_filters('opportunities_details_tabs', $tabs);
}

function leads_details_tabs($id)
{
	$url = 'admin/leads/leads_details/';

	$tabs = array(
		'leads_details' => [
			'position' => 1,
			'name' => 'leads_details',
			'url' => $url . $id,
			'count' => '',
			'view' => 'admin/leads/leads_details/index'
		],
		'call' => [
			'position' => 2,
			'name' => 'call',
			'url' => $url . $id . '/call',
			'count' => total_rows('tbl_calls', array('leads_id' => $id)),
			'view' => 'admin/common/call_log'
		],
		'mettings' => [
			'position' => 3,
			'name' => 'mettings',
			'url' => $url . $id . '/mettings',
			'count' => total_rows('tbl_mettings', array('leads_id' => $id)),
			'view' => 'admin/common/meeting_log'
		],
		'comments' => [
			'position' => 4,
			'name' => 'comments',
			'url' => $url . $id . '/comments',
			'count' => total_rows('tbl_task_comment', array('module' => 'leads', 'module_field_id' => $id)),
			'view' => 'admin/common/comments'
		],
		'attachments' => [
			'position' => 5,
			'name' => 'attachments',
			'url' => $url . $id . '/attachments',
			'count' => total_rows('tbl_attachments', array('module' => 'leads', 'module_field_id' => $id)),
			'view' => 'admin/common/attachments'
		],
		'tasks' => [
			'position' => 6,
			'name' => 'tasks',
			'url' => $url . $id . '/tasks',
			'count' => total_rows('tbl_task', array('leads_id' => $id)),
			'view' => $url . 'tasks'
		],
		'proposals' => [
			'position' => 7,
			'name' => 'proposals',
			'url' => $url . $id . '/proposals',
			'count' => total_rows('tbl_proposals', array('module_id' => $id)),
			'view' => $url . 'proposals'
		],
		'reminder' => [
			'position' => 8,
			'name' => 'reminder',
			'url' => $url . $id . '/reminder',
			'count' => total_rows('tbl_reminders', array('module' => 'leads', 'module_id' => $id)),
			'view' => 'admin/common/reminder_log'
		],
		'notes' => [
			'position' => 9,
			'name' => 'notes',
			'url' => $url . $id . '/notes',
			'count' => '',
			'view' => 'admin/common/notes_log'
		],
		'activities' => [
			'position' => 10,
			'name' => 'activities',
			'url' => $url . $id . '/activities',
			'count' => total_rows('tbl_activities', array('module' => 'leads', 'module_field_id' => $id)),
			'view' => 'admin/common/activites'
		],
	);
	return apply_filters('leads_details_tabs', $tabs);
}

function projects_details_tabs($id, $tab_style = null, $active_tab = null, $tab_counts = array())
{
	$cache_key = 'projects_details_tabs_' . (int) $id . '_' . md5(json_encode(array(
		'tab_style' => $tab_style,
		'active_tab' => $active_tab,
		'tab_counts' => (array) $tab_counts,
	)));
	$cached_tabs = helper_cache_get($cache_key);
	if ($cached_tabs !== false) {
		return $cached_tabs;
	}
	// make details tab array and assign order,name,url,count
	$url = 'admin/projects/project_details/';
	$tab_counts = is_array($tab_counts) ? $tab_counts : array();
	$tabs = array(
		'project_details' => array(
			'position' => 1,
			'name' => 'project_details',
			'url' => $url . $id,
			'count' => '',
			'icon' => 'folder-open',
			'view' => 'admin/projects/project_details/details'
		),
		'comments' => [
			'position' => 2,
			'name' => 'comments',
			'url' => $url . $id . '/comments',
			'count' => isset($tab_counts['comments']) ? (int) $tab_counts['comments'] : '',
			'icon' => 'message-circle',
			'view' => 'admin/common/comments'
		],
		'attachments' => [
			'position' => 3,
			'name' => 'attachments',
			'url' => $url . $id . '/attachments',
			'count' => isset($tab_counts['attachments']) ? (int) $tab_counts['attachments'] : '',
			'icon' => 'paperclip',
			'view' => 'admin/common/attachments'
		],
		'calender' => array(
			'position' => 4,
			'name' => 'calender',
			'url' => $url . $id . '/calender',
			'count' => '',
			'icon' => 'calendar',
			'view' => $url . 'calender',
		),
		'milestones' => array(
			'position' => 5,
			'name' => 'milestones',
			'url' => $url . $id . '/milestones',
			// 'count' => total_rows('tbl_milestones', array('project_id' => $id)),
			'icon' => 'flag',
			'view' => $url . 'milestones',
		),
		'tasks' => array(
			'position' => 6,
			'name' => 'tasks',
			'url' => $url . $id . '/tasks',
			// 'count' => total_rows('tbl_task', array('project_id' => $id)),
			'icon' => 'check-square',
			'view' => $url . 'tasks',
		),
		'bugs' => array(
			'position' => 7,
			'name' => 'bugs',
			'url' => $url . $id . '/bugs',
			// 'count' => total_rows('tbl_bug', array('project_id' => $id)),
			'icon' => 'bug',
			'view' => $url . 'bugs',
		),
		'gantt' => array(
			'position' => 8,
			'name' => 'gantt',
			'url' => $url . $id . '/gantt',
			'count' => '',
			'icon' => 'bar-chart-3',
			'view' => $url . 'gantt',
		),
		'notes' => [
			'position' => 9,
			'name' => 'notes',
			'url' => $url . $id . '/notes',
			'count' => '',
			'icon' => 'sticky-note',
			'view' => 'admin/common/notes_log'
		],
		'timesheet' => array(
			'position' => 10,
			'name' => 'timesheet',
			'url' => $url . $id . '/timesheet',
			// 'count' => total_rows('tbl_tasks_timer', array('project_id' => $id)),
			'icon' => 'clock',
			'view' => $url . 'timesheet',
		),
		'tickets' => array(
			'position' => 11,
			'name' => 'tickets',
			'url' => $url . $id . '/tickets',
			// 'count' => total_rows('tbl_tickets', array('project_id' => $id)),
			'icon' => 'ticket',
			'view' => $url . 'tickets',
		),
		'sales' => array(
			'position' => 12,
			'name' => 'sales',
			'url' => '#',
			'count' => '',
			'icon' => 'line-chart',
			'children' => array(
				'invoice' => array(
					'position' => 1,
					'name' => 'invoice',
					'url' => $url . $id . '/invoice',
					'icon' => 'file-text',
					'view' => $url . 'invoice',
				),
				'credit_note' => array(
					'position' => 2,
					'name' => 'credit_note',
					'url' => $url . $id . '/credit_note',
					'icon' => 'rotate-ccw',
					'view' => $url . 'credit_note',
				),
				'estimates' => array(
					'position' => 3,
					'name' => 'estimates',
					'url' => $url . $id . '/estimates',
					'icon' => 'calculator',
					'view' => $url . 'estimates',
				),
				'expense' => array(
					'position' => 4,
					'name' => 'expense',
					'url' => $url . $id . '/expense',
					'count' => isset($tab_counts['expense']) ? (int) $tab_counts['expense'] : '',
					'icon' => 'credit-card',
					'view' => $url . 'expense',
				),
				'deposit' => array(
					'position' => 5,
					'name' => 'deposit',
					'url' => $url . $id . '/deposit',
					'count' => isset($tab_counts['deposit']) ? (int) $tab_counts['deposit'] : '',
					'icon' => 'arrow-down',
					'view' => $url . 'deposit',
				),
				'purchase' => array(
					'position' => 6,
					'name' => 'purchase',
					'url' => $url . $id . '/purchase',
					'icon' => 'shopping-cart',
					'view' => $url . 'purchase',
				),
				'contract' => array(
					'position' => 7,
					'name' => 'contract',
					'url' => $url . $id . '/contract',
					'icon' => 'file-text',
					'view' => $url . 'contract',
				),
			),
		),
		'project_settings' => array(
			'position' => 16,
			'name' => 'project_settings',
			'url' => $url . $id . '/project_settings',
			'count' => '',
			'icon' => 'settings',
			'view' => $url . 'project_settings',
		),
		'activities' => [
			'position' => 17,
			'name' => 'activities',
			'url' => $url . $id . '/activities',
			'count' => total_rows('tbl_activities', array('module' => 'projects', 'module_field_id' => $id)),
			'icon' => 'history',
			'view' => 'admin/common/activites'
		],

	);
	if (!empty($tab_style)) {
		$tabs['_meta'] = array('tab_style' => $tab_style);
	}
	$result = apply_filters('projects_details_tabs', $tabs);
	helper_cache_save($cache_key, $result, 60);
	return $result;
}

function client_details_tabs($id, $tab_style = null)
{
	$cache_key = 'client_details_tabs_' . (int) $id . '_' . md5((string) $tab_style);
	$cached_tabs = helper_cache_get($cache_key);
	if ($cached_tabs !== false) {
		return $cached_tabs;
	}
	// make details tab array and assign order,name,url,count
	$url = 'admin/client/details/';
	$tabs = array(
		'details' => [
			'position' => 1,
			'name' => 'details',
			'url' => $url . $id,
			'count' => '',
			'icon' => 'info',
			'view' => 'admin/client/details'
		],
		'contacts' => [
			'position' => 2,
			'name' => 'contacts',
			'url' => $url . $id . '/contacts',
			'count' => total_rows('tbl_account_details', array('company' => $id)),
			'icon' => 'users',
			'view' => $url . 'contacts',
		],
		'notes' => [
			'position' => 3,
			'name' => 'notes',
			'url' => $url . $id . '/notes',
			'count' => '',
			'icon' => 'sticky-note',
			'view' => 'admin/common/notes_log'
		],
		'invoices' => [
			'position' => 4,
			'name' => 'invoices',
			'url' => $url . $id . '/invoices',
			'count' => total_rows('tbl_invoices', array('client_id' => $id)),
			'icon' => 'file-text',
			'view' => $url . 'invoices',
		],
		'payments' => [
			'position' => 5,
			'name' => 'payments',
			'url' => $url . $id . '/payments',
			'count' => total_rows('tbl_payments', array('paid_by' => $id)),
			'icon' => 'receipt',
			'view' => $url . 'payments',
		],
		'estimates' => [
			'position' => 6,
			'name' => 'estimates',
			'url' => $url . $id . '/estimates',
			'count' => total_rows('tbl_estimates', array('client_id' => $id)),
			'icon' => 'calculator',
			'view' => $url . 'estimates',
		],
		'proposals' => [
			'position' => 6,
			'name' => 'proposals',
			'url' => $url . $id . '/proposals',
			'count' => total_rows('tbl_proposals', array('module' => 'client', 'module_id' => $id)),
			'icon' => 'file-check',
			'view' => $url . 'proposals',
		],
		'transactions' => [
			'position' => 7,
			'name' => 'transactions',
			'url' => $url . $id . '/transactions',
			'count' => total_rows('tbl_transactions', array('paid_by' => $id)),
			'icon' => 'list',
			'view' => $url . 'transactions',
		],
		'projects' => [
			'position' => 8,
			'name' => 'projects',
			'url' => $url . $id . '/projects',
			'count' => total_rows('tbl_project', array('client_id' => $id)),
			'icon' => 'folder-kanban',
			'view' => $url . 'projects',
		],
		'tickets' => [
			'position' => 9,
			'name' => 'tickets',
			'url' => $url . $id . '/tickets',
			'count' => '',
			'icon' => 'ticket',
			'view' => $url . 'tickets',
		],
		'contracts' => [
			'position' => 10,
			'name' => 'contracts',
			'url' => $url . $id . '/contracts',
			'count' => '',
			'icon' => 'file-text',
			'view' => $url . 'contracts',
		],
		'bugs' => [
			'position' => 10,
			'name' => 'bugs',
			'url' => $url . $id . '/bugs',
			'count' => '',
			'icon' => 'bug',
			'view' => $url . 'bugs',
		],
		'award_point' => [
			'position' => 11,
			'name' => 'award_point',
			'url' => $url . $id . '/award_point',
			'count' => '',
			'icon' => 'award',
			'view' => $url . 'award_point',
		],
		'filemanager' => [
			'position' => 12,
			'name' => 'filemanager',
			'url' => $url . $id . '/filemanager',
			'count' => '',
			'icon' => 'folder',
			'view' => $url . 'filemanager',
		],
		'map' => [
			'position' => 13,
			'name' => 'map',
			'url' => $url . $id . '/map',
			'count' => '',
			'icon' => 'map-pin',
			'view' => $url . 'map',
		],

	);
	if (!empty($tab_style)) {
		$tabs['_meta'] = array('tab_style' => $tab_style);
	}
	$result = apply_filters('client_details_tabs', $tabs);
	helper_cache_save($cache_key, $result, 60);
	return $result;
}

function tab_load_view($all_tab, $active)
{
	foreach ($all_tab as $key => $tab) {
		if (isset($tab['name']) && $tab['name'] == $active && isset($tab['view'])) {
			return $tab['view'];
		}
		if (isset($tab['children']) && is_array($tab['children'])) {
			foreach ($tab['children'] as $child) {
				if (isset($child['name']) && $child['name'] == $active && isset($child['view'])) {
					return $child['view'];
				}
			}
		}
	}
	return false;
}

function render_activities($module, $id, $style = null)
{
	$ci = &get_instance();
	if (empty($module) || empty($id)) {
		return '';
	}
	$data = array(
		'module' => $module,
		'id' => $id,
		'activity_style' => config_item('activity_style') ?: 'timeline',
	);
	return $ci->load->view('admin/common/activites', $data, true);
}

function tasks_details_tabs($id)
{
	// make details tab array and assign order,name,url,count
	$url = 'admin/tasks/details/';
	$tabs = array(
		'details' => [
			'position' => 1,
			'name' => 'details',
			'url' => $url . $id,
			'count' => '',
			'view' => 'admin/tasks/details/index'
		],
		'comments' => [
			'position' => 2,
			'name' => 'comments',
			'url' => $url . $id . '/comments',
			'count' => total_rows('tbl_task_comment', array('module' => 'tasks', 'module_field_id' => $id)),
			'view' => 'admin/common/comments'
		],
		'attachments' => [
			'position' => 3,
			'name' => 'attachments',
			'url' => $url . $id . '/attachments',
			'count' => total_rows('tbl_attachments', array('module' => 'tasks', 'module_field_id' => $id)),
			'view' => 'admin/common/attachments'
		],
		'timesheets' => [
			'position' => 4,
			'name' => 'timesheets',
			'url' => $url . $id . '/timesheets',
			'count' => total_rows('tbl_tasks_timer', array('task_id' => $id, 'start_time !=' => 0, 'end_time !=' => 0)),
			'view' => $url . 'timesheets'
		],
		'notes' => [
			'position' => 5,
			'name' => 'notes',
			'url' => $url . $id . '/notes',
			'count' => '',
			// 'view' => $url . 'notes'
			'view' => 'admin/common/notes_log'
		],
		'sub_tasks' => [
			'position' => 6,
			'name' => 'sub_tasks',
			'url' => $url . $id . '/sub_tasks',
			'count' => total_rows('tbl_task', array('sub_task_id' => $id)),
			'view' => $url . 'sub_tasks'
		],
		'activites' => [
			'position' => 7,
			'name' => 'activites',
			'url' => $url . $id . '/activites',
			'count' => total_rows('tbl_activities', array('module' => 'tasks', 'module_field_id' => $id)),
			'view' => 'admin/common/activites'
		],
	);
	return apply_filters('tasks_details_tabs', $tabs);
}

function user_details_tabs($id)
{
	// make details tab array and assign order,name,url,count
	$url = 'admin/user/user_details/';
	$tabs = array(
		'user_details' => [
			'position' => 1,
			'name' => 'user_details',
			'url' => $url . $id,
			'count' => '',
			'view' => 'admin/user/user_details/index'
		],
		'bank' => [
			'position' => 2,
			'name' => 'bank',
			'url' => $url . $id . '/bank',
			'count' => total_rows('tbl_employee_bank', array('user_id' => $id)),
			'view' => $url . 'bank_details'
		],
		'document_details' => [
			'position' => 3,
			'name' => 'document_details',
			'url' => $url . $id . '/document_details',
			'count' => total_rows('tbl_employee_document', array('user_id' => $id)),
			'view' => $url . 'document_details'
		],
		'shift_details' => [
			'position' => 3,
			'name' => 'shift_details',
			'url' => $url . $id . '/shift_details',
			'count' => '',
			'view' => $url . 'shift_details'
		],
		'salary_details' => [
			'position' => 4,
			'name' => 'salary_details',
			'url' => $url . $id . '/salary_details',
			'count' => '',
			'view' => 'admin/user/salary_details'
		],
		'timecard_details' => [
			'position' => 5,
			'name' => 'timecard_details',
			'url' => $url . $id . '/timecard_details',
			'count' => '',
			'view' => 'admin/user/timecard_details'
		],
		'leave_details' => [
			'position' => 6,
			'name' => 'leave_details',
			'url' => $url . $id . '/leave_details',
			'count' => '',
			'view' => 'admin/user/leave_details'
		],
		'provident_found' => [
			'position' => 7,
			'name' => 'provident_found',
			'url' => $url . $id . '/provident_found',
			'count' => '',
			'view' => 'admin/user/provident_found'
		],
		'Overtime_details' => [
			'position' => 8,
			'name' => 'Overtime_details',
			'url' => $url . $id . '/Overtime_details',
			'count' => '',
			'view' => 'admin/user/Overtime_details'
		],
		'tasks' => [
			'position' => 9,
			'name' => 'tasks',
			'url' => $url . $id . '/tasks',
			'count' => '',
			'view' => 'admin/user/tasks_details'
		],
		'projects' => [
			'position' => 10,
			'name' => 'projects',
			'url' => $url . $id . '/projects',
			'count' => '',
			'view' => 'admin/user/projects_details'
		],
		'bugs' => [
			'position' => 11,
			'name' => 'bugs',
			'url' => $url . $id . '/bugs',
			'count' => '',
			'view' => 'admin/user/bugs_details'
		],
		'user' => [
			'position' => 12,
			'name' => 'user',
			'url' => $url . $id . '/user',
			'count' => '',
			'view' => 'admin/user/user_awardpoints'
		],
		'notifications' => [
			'position' => 13,
			'name' => 'notifications',
			'url' => $url . $id . '/notifications',
			'count' => '',
			'view' => $url . 'notifications'
		],
		'activities' => [
			'position' => 14,
			'name' => 'activities',
			'url' => $url . $id . '/activities',
			'count' => total_rows('tbl_activities', array('module' => 'user', 'module_field_id' => $id)),
			'view' => 'admin/common/activites'
		],

	);
	return apply_filters('user_details_tabs', $tabs);
}

function transactions_details_tabs($id)
{
	// make details tab array and assign order,name,url,count
	$url = 'admin/transactions/view_details/';
	$tabs = array(
		'details' => [
			'position' => 1,
			'name' => 'details',
			'url' => $url . $id,
			'count' => '',
			'view' => $url . 'details'
		],
		'tasks' => [
			'position' => 2,
			'name' => 'tasks',
			'url' => $url . $id . '/tasks',
			'count' => total_rows('tbl_task', array('transactions_id' => $id)),
			'view' => $url . 'tasks',
			'icon' => 'list-todo'
		],
		'reminder' => [
			'position' => 3,
			'name' => 'reminder',
			'url' => $url . $id . '/reminder',
			'count' => total_rows('tbl_reminders', array('module_id' => $id)),
			'view' => $url . 'reminder',
			'icon' => 'bell-ring'
		],


	);
	return apply_filters('transactions_details_tabs', $tabs);
}

function goal_details_tabs($id)
{
	// make details tab array and assign order,name,url,count
	$url = 'admin/goal_tracking/goal_details/';
	$tabs = array(
		'details' => [
			'position' => 1,
			'name' => 'details',
			'url' => $url . $id,
			'count' => '',
			'view' => $url . 'details'
		],
		'tasks' => [
			'position' => 2,
			'name' => 'tasks',
			'url' => $url . $id . '/tasks',
			'count' => total_rows('tbl_task', array('goal_tracking_id' => $id)),
			'view' => $url . 'task'
		],
		'comments' => [
			'position' => 3,
			'name' => 'comments',
			'url' => $url . $id . '/comments',
			'count' => total_rows('tbl_task_comment', array('module' => 'goal_tracking', 'module_field_id' => $id)),
			'view' => 'admin/common/comments'
		],
		'activities' => [
			'position' => 4,
			'name' => 'activities',
			'url' => $url . $id . '/activities',
			'count' => total_rows('tbl_activities', array('module' => 'goal_tracking', 'module_field_id' => $id)),
			'view' => 'admin/common/activites'
		],


	);
	return apply_filters('goal_details_tabs', $tabs);
}

function client_project_details_tabs($id)
{
	$url = 'client/projects/project_details/';
	$tabs = array(
		'project_details' => array(
			'position' => 1,
			'name' => 'project_details',
			'url' => $url . $id,
			'count' => '',
			'view' => $url . 'details'
		),
		'calender' => array(
			'position' => 2,
			'name' => 'calender',
			'url' => $url . $id . '/calender',
			'count' => '',
			'view' => $url . 'calender',
		),

		'comments' => [
			'position' => 3,
			'name' => 'comments',
			'url' => $url . $id . '/comments',
			'count' => total_rows('tbl_task_comment', array('module' => 'projects', 'module_field_id' => $id)),
			// 'view' => $url . 'comments'
			'view' => 'admin/common/comments',
		],
		'attachments' => [
			'position' => 4,
			'name' => 'attachments',
			'url' => $url . $id . '/attachments',
			'count' => total_rows('tbl_attachments', array('module' => 'projects', 'module_field_id' => $id)),
			// 'view' => $url . 'attachments'
			'view' => 'admin/common/attachments'
		],
		'timesheet' => [
			'position' => 5,
			'name' => 'timesheet',
			'url' => $url . $id . '/timesheet',
			'count' => '',
			'view' => $url . 'timesheet'
		],
		'milestones' => [
			'position' => 6,
			'name' => 'milestones',
			'url' => $url . $id . '/milestones',
			'count' => total_rows('tbl_milestones', array('project_id' => $id)),
			'view' => $url . 'milestones'
		],
		'tasks' => [
			'position' => 7,
			'name' => 'tasks',
			'url' => $url . $id . '/tasks',
			'count' => total_rows('tbl_task', array('project_id' => $id)),
			'view' => $url . 'task'
		],
		'bugs' => [
			'position' => 8,
			'name' => 'bugs',
			'url' => $url . $id . '/bugs',
			'count' => total_rows('tbl_bug', array('project_id' => $id)),
			'view' => $url . 'bugs'
		],
		'gantt' => [
			'position' => 9,
			'name' => 'gantt',
			'url' => $url . $id . '/gantt',
			'count' => '',
			'view' => $url . 'gantt_chart'
		],
		'tickets' => [
			'position' => 10,
			'name' => 'tickets',
			'url' => $url . $id . '/tickets',
			'count' => total_rows('tbl_tickets', array('project_id' => $id)),
			'view' => $url . 'tickets'
		],
		'invoice' => [
			'position' => 11,
			'name' => 'invoice',
			'url' => $url . $id . '/invoice',
			'count' => total_rows('tbl_invoices', array('project_id' => $id)),
			'view' => $url . 'invoice'
		],
		'estimates' => [
			'position' => 12,
			'name' => 'estimates',
			'url' => $url . $id . '/estimates',
			'count' => total_rows('tbl_estimates', array('project_id' => $id)),
			'view' => $url . 'estimates'
		],
		'activities' => [
			'position' => 13,
			'name' => 'activities',
			'url' => $url . $id . '/activities',
			'count' => total_rows('tbl_activities', array('module' => 'project', 'module_field_id' => $id)),
			// 'view' => $url . 'activities'
			'view' => 'admin/common/activites'
		],


	);
	return apply_filters('client_project_details_tabs', $tabs);
}

function client_bugs_details_tabs($id)
{
	$url = 'client/bugs/view_bug_details/';
	$tabs = array(
		'details' => array(
			'position' => 1,
			'name' => 'details',
			'url' => $url . $id,
			'count' => '',
			'view' => 'client/bugs/task_details'
		),
		'comments' => array(
			'position' => 2,
			'name' => 'comments',
			'url' => $url . $id . '/comments',
			'count' => total_rows('tbl_task_comment', array('module_field_id' => $id)),
			// 'view' => 'client/bugs/task_comments'
			'view' => 'admin/common/comments'
		),
		'attachments' => array(
			'position' => 3,
			'name' => 'attachments',
			'url' => $url . $id . '/attachments',
			'count' => total_rows('tbl_attachments', array('module' => 'bugs', 'module_field_id' => $id)),
			'view' => 'admin/common/attachments'
		),
		'activities' => array(
			'position' => 4,
			'name' => 'activities',
			'url' => $url . $id . '/activities',
			'count' => total_rows('tbl_activities', array('module' => 'bugs', 'module_field_id' => $id)),
			'view' => 'admin/common/activites'
		),
	);
	return apply_filters('client_bugs_details_tabs', $tabs);
}

function join_data($table, $select = '*', $where = null, $join = null, $row = null, $order_by = null)
{
	$CI = &get_instance();
	if ($select == '*') {
		$CI->db->select('*', false);
	} else {
		$CI->db->select("$select", false);
	}
	$CI->db->from($table);
	if (!empty($join)) {
		foreach ($join as $tbl => $wh) {
			$CI->db->join($tbl, $wh, 'left');
		}
	}
	if (!empty($where)) {
		$CI->db->where($where);
	}
	$CI->admin_model->staff_query($table);
	if (!empty($order_by)) {
		foreach ($order_by as $key => $value) {
			$CI->db->order_by($key, $value);
		}
	}
	$query = $CI->db->get();
	if (!empty($row) && $row === 'array') {
		$result = $query->result_array();
	} else if (!empty($row) && $row === 'object') {
		$result = $query->result();
	} else {
		$result = $query->row();
	}
	return $result;
}

function get_sales_currency($sales_info)
{
	if (!empty($sales_info->system_currency)) {
		$currency = get_row('tbl_currencies', array('code' => $sales_info->system_currency));
	} else {
		$currency = get_row('tbl_currencies', array('code' => config_item('default_currency')));
	}
	if (!empty($currency)) {
		$sales_info->main_currency_code = $currency->code;
		$sales_info->symbol = $currency->symbol;
	} else {
		$sales_info->main_currency_code = 'USD';
		$sales_info->symbol = '$';
	}
	$currency = apply_filters('sales_currency', $sales_info);
	return $currency;
}

function generate_qrcode($data, $number = 6)
{
	$CI = &get_instance();
	$CI->load->library('qrcode');
	$qr = new QRCode();
	// create QR Code with image
	$qr->setErrorCorrectLevel(QR_ERROR_CORRECT_LEVEL_L);
	$qr->setTypeNumber($number);
	$qr->addData($data);
	$qr->make();
	// create image string code and return as img src
	$image = $qr->createImage(2, 4);
	ob_start();
	imagegif($image);
	$image_string = base64_encode(ob_get_contents());
	ob_end_clean();
	return '<img src="data:image/gif;base64,' . $image_string . '" />';
}

if (!function_exists('strtolower')) {
	function strtolower($str) {}
}

if (!function_exists('mention_text')) {
	function mention_text($value)
	{
		if ($value === null) {
			return '';
		}
		$escaped = html_escape($value);
		return preg_replace_callback(
			'/@(\d+)/',
			function ($matches) {
				$user_id = (int) $matches[1];
				$url = base_url('admin/user/user_details/' . $user_id);
				return '<a href="' . $url . '" class="text-primary hover:underline">@' . $user_id . '</a>';
			},
			$escaped
		);
	}
}

if (!function_exists('normalize_project_settings')) {
	function normalize_project_settings($settings)
	{
		$legacy_map = array(
			'view_task' => 'show_project_tasks',
			'bug' => 'show_project_bugs',
			'milestone' => 'show_milestones',
			'attachment' => 'show_project_attachments',
			'calendar' => 'show_project_calendar',
			'comments' => 'show_project_comments',
			'history' => 'show_project_history',
			'gantt' => 'show_gantt_chart',
			'project_hours' => 'show_project_hours',
			'task_hours' => 'show_tasks_hours',
			'finance_overview' => 'show_finance_overview',
			'staff_finance_overview' => 'show_staff_finance_overview',
		);

		if (empty($settings)) {
			return array();
		}

		if (is_string($settings)) {
			$decoded = json_decode($settings, true);
			if (json_last_error() === JSON_ERROR_NONE) {
				$settings = $decoded;
			} else {
				return array();
			}
		}

		if (is_object($settings)) {
			$settings = (array) $settings;
		}

		if (!is_array($settings)) {
			return array();
		}

		$normalized = array();
		foreach ($settings as $key => $value) {
			$setting_name = is_string($key) && !is_numeric($key) ? $key : $value;
			if (!is_string($setting_name) || $setting_name === '') {
				continue;
			}
			if (isset($legacy_map[$setting_name])) {
				$setting_name = $legacy_map[$setting_name];
			}
			$normalized[] = $setting_name;
		}

		return array_values(array_unique($normalized));
	}
}

if (!function_exists('project_settings_post')) {
	function project_settings_post($field_rows)
	{
		$CI = &get_instance();
		$settings = array();

		if (!is_array($field_rows)) {
			return $settings;
		}

		foreach ($field_rows as $row) {
			if (empty($row->settings_id)) {
				continue;
			}
			$value = $CI->input->post($row->settings_id, true);
			if (!empty($value)) {
				$settings[] = $value;
			}
		}

		return array_values(array_unique($settings));
	}
}

function permission_post($field_name = 'permission')
{
	$CI = &get_instance();
	$permission = $CI->input->post($field_name, true);
	if (!empty($permission)) {
		if ($permission == 'everyone') {
			$assigned = 'all';
		} else {
			$assigned_to = $CI->input->post('assigned_to', true);
			if (!empty($assigned_to)) {
				foreach ($assigned_to as $assign_user) {
					$assigned[$assign_user] = array();
					$view = 'perm_view_' . $assign_user;
					$edit = 'perm_edit_' . $assign_user;
					$delete = 'perm_delete_' . $assign_user;
					if ($CI->input->post($view, true) == 1) {
						$assigned[$assign_user][] = 'view';
					}
					if ($CI->input->post($edit, true) == 1) {
						// if view not selected yet, then add view first
						if (!in_array('view', $assigned[$assign_user])) {
							$assigned[$assign_user][] = 'view';
						}
						$assigned[$assign_user][] = 'edit';
					}
					if ($CI->input->post($delete, true) == 1) {
						$assigned[$assign_user][] = 'delete';
					}
				}
			}
		}
		if (!empty($assigned)) {
			if ($assigned != 'all') {
				$assigned = json_encode($assigned);
			}
		} else {
			$assigned = 'all';
		}
		return $assigned;
	} else {
		set_message('error', lang('assigned_to') . ' Field is required');
		if (empty($_SERVER['HTTP_REFERER'])) {
			redirect('admin/dashboard');
		} else {
			redirect($_SERVER['HTTP_REFERER']);
		}
	}
}


function form_grouped_select_group($name, $label, array $groups = array(), $selected = '', $required = false, array $attrs = array(), $prepend = '', $append = '', $layout = 'stacked')
{
	$id = isset($attrs['id']) ? $attrs['id'] : $name;
	$classes = 'w-full rounded-lg border border-border bg-input px-3 py-2 text-sm dark:focus:border-secondary dark:focus:ring-3 dark:focus:ring-secondary/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-secondary/30 flex items-center justify-between tw-selectpicker';
	if (!empty($attrs['class'])) {
		$classes .= ' ' . $attrs['class'];
	}
	if ($prepend) {
		$classes .= ' rounded-l-none ';
	}
	if ($append) {
		$classes .= ' rounded-r-none ';
	}
	$attrs['id'] = $id;
	$attrs['name'] = $name;
	$attrs['class'] = $classes;
	if (empty($attrs['data-placeholder'])) {
		$attrs['data-placeholder'] = lang('nothing_selected') ?: 'Nothing selected';
	}
	if ($required) {
		$attrs['required'] = 'required';
	}
	$attrString = build_html_attributes($attrs);
	$badge = $required ? ' <span class="text-red-500">*</span>' : '';

	$selected_values = is_array($selected)
		? array_map('strval', $selected)
		: array((string) $selected);

	$optsHtml = '';
	foreach ($groups as $group) {
		$group_label = '';
		$items = array();
		if (is_array($group)) {
			$group_label = isset($group['label']) ? $group['label'] : '';
			$items = isset($group['items']) && is_array($group['items']) ? $group['items'] : array();
		} elseif (is_object($group)) {
			$group_label = isset($group->label) ? $group->label : '';
			$items = isset($group->items) && is_array($group->items) ? $group->items : array();
		}
		if ($group_label === '' || empty($items)) {
			continue;
		}
		$optsHtml .= '<optgroup label="' . html_escape($group_label) . '">';
		foreach ($items as $val => $text) {
			$sel = in_array((string) $val, $selected_values, true) ? ' selected' : '';
			$optsHtml .= '<option value="' . html_escape($val) . '"' . $sel . '>' . html_escape($text) . '</option>';
		}
		$optsHtml .= '</optgroup>';
	}

	$inputEl = '<select ' . $attrString . '>' . $optsHtml . '</select>';
	if ($prepend || $append) {
		$inputEl = '<div class="flex items-start">'
			. ($prepend ? '<span class="inline-flex group cursor-pointer h-[38px] w-10 items-center justify-center text-sm bg-background text-muted-foreground transition-colors hover:bg-muted hover:text-foreground rounded-l-lg border border-r-0 border-border bg-input">' . $prepend . '</span>' : '')
			. '<div class="flex-1">' . $inputEl . '</div>'
			. ($append ? '<span class="inline-flex group cursor-pointer h-[38px] w-10 items-center justify-center text-sm bg-background text-muted-foreground transition-colors hover:bg-muted hover:text-foreground rounded-r-lg border border-l-0 border-border bg-input">' . $append . '</span>' : '')
			. '</div>';
	}

	if ($layout === 'horizontal') {
		return '<div class="flex items-start gap-3 py-2">'
			. '<label for="' . html_escape($id) . '" class="w-48 text-sm font-medium text-foreground pt-2 text-right">' . $label . $badge . '</label>'
			. '<div class="flex-1">' . $inputEl . '</div>'
			. '</div>';
	}

	return '<label class="space-y-1 text-sm font-medium text-foreground block">'
		. '<span>' . $label . $badge . '</span>'
		. $inputEl
		. '</label>';
}
