```
<?php
declare(strict_types=1);

namespace app\helpers;

use Yii;
use yii\helpers\Html;
use yii\helpers\Url;
use yii\helpers\ArrayHelper;
use yii\helpers\StringHelper;

/**
* View Helper
*/
class ViewHelper
{
	/**
	* Truncates a string and adds a tooltip with full text.
	* @param string $text The text to truncate.
	* @param int $length Number of characters to truncate at (default 30).
	* @return string HTML span with truncated text and tooltip.
	* How to use:
	*
	* 	use app\helpers\ViewHelper;
	*
	* Use either:
	*
	* 	[
	* 		'attribute' => 'name',
	* 		'format' => 'raw',
	* 		'value' => fn($model) => ViewHelper::renderText($model->description, 20, false),
	* 	],
	*	
	* or
	*
	* 	[
	* 		'attribute' => 'name',
	* 		'format' => 'raw',
	* 		'value' => function($model)
	*		{
	*			return ViewHelper::renderText($model->description, 20, false);
	*		},
	* 	],
	*/
	public static function renderText(?string $text, int $length = 30, bool $withTooltip = true): string
	{
		if ($text === null || $text === '')
		{
			return Html::tag('span', \Yii::t('app', '(not set)'), ['class' => 'not-set text-muted fst-italic']);
		}
	
		$encoded = Html::encode($text);
		
		if (mb_strlen($encoded) <= $length)
		{
			return $encoded;
		}
		
		$truncated = StringHelper::truncate($encoded, $length);
		
		if (!$withTooltip)
		{
			return $truncated;
		}
		
		return Html::tag('span', $truncated, [
			'title' => $encoded,
			'data-bs-toggle' => 'tooltip',
			'style' => 'cursor: help;'
		]);
	}
	
	/**
	* Renders a URL as a clickable link if valid, otherwise as plain text or an invalid indicator.
	* @param string|null $url The URL to render.
	* @param string|null $label Optional label for the link; if omitted, the URL is used as label.
	* @return string Safe HTML anchor tag or encoded text or invalid URL placeholder.
	* How to use:
	* 
	* 	[
	* 		'attribute' => 'website',
	* 		'format' => 'raw',
	* 		'value' => fn($model) => ViewHelper::renderLinkOrText($model->website, 'Visit Website'),
	* 	],
	*/
	public static function renderLinkOrText(?string $url, ?string $label = null): string
	{
		if (filter_var($url, FILTER_VALIDATE_URL))
		{
			return Html::a(Html::encode($label ?? $url), $url, ['target' => '_blank', 'rel' => 'noopener noreferrer']);
		}
		
		if ($label)
		{
			return Html::encode($label);
		}
		
		return Html::tag('span', \Yii::t('app', '(invalid URL)'), ['class' => 'invalid-url']);
	}
	
	/**
	* Render a related user (e.g. createdBy or updatedBy) with fallback
	* If no name, fallback to email. If neither, mark as not set.
	* How to use:
	* 
	*	[
	* 		'attribute' => 'created_by',
	* 		'format' => 'raw',
	* 		'value' => fn($model) => ViewHelper::renderRelatedUser($model, 'createdBy'),
	* 	],[
	* 		'attribute' => 'updated_by',
	* 		'format' => 'raw',
	* 		'value' => fn($model) => ViewHelper::renderRelatedUser($model, 'updatedBy'),
	* 	],
	*/
	public static function renderRelatedUser($model, string $relation): string
	{
		$user = ArrayHelper::getValue($model, $relation);
		
		if (!$user)
		{
			return Html::tag('span', \Yii::t('app', '(not set)'), ['class' => 'not-set text-muted fst-italic']);
		}
		
		$fullname = trim(($user->firstname ?? '') . ' ' . ($user->lastname ?? ''));
		$display = $fullname !== '' ? $fullname : ($user->email ?? '');
		
		if ($display === '')
		{
			return Html::tag('span', \Yii::t('app', '(not set)'), ['class' => 'not-set text-muted fst-italic']);
		}
		
		return Html::encode($display);
	}
	
	/**
	* Render an image URL as a clickable link in DetailView.
	* @param string $url The URL to be rendered as a link.
	* @return string Safe HTML anchor tag or encoded URL if invalid.
	* How to use:
	* 
	* 	[
	* 		'attribute' => 'image_header',
	* 		'format' => 'raw',
	* 		'value' => fn($model) => ViewHelper::renderImageLink($model->image_header),
	* 	],
	*/
	public static function renderImageLink(string $url): string
	{
		$encodedUrl = Html::encode($url);
		$href = Url::to($url);
		
		return Html::a($encodedUrl, $href, ['target' => '_blank', 'rel' => 'noopener noreferrer']);
	}
	
	/**
	* Render a thumbnail image, assuming the image filename derived from URL.
	* @param string $imageUrl Full image URL or path
	* @param string|null $alt Alt attribute for image tag
	* @param array $options Additional HTML options for img tag
	* @return string
	* How to use:
	* 
	*	[
	* 		'attribute' => 'product_image_one',
	* 		'format' => 'raw',
	* 		'value' => fn($model) => ViewHelper::renderThumbnail($model->product_image_one),
	* 	],
	*/
	public static function renderThumbnail(string $imageUrl, ?string $alt = null, array $options = []): string
	{
		$url = basename(parse_url($imageUrl, PHP_URL_PATH));
		$alt = $alt ?: $url;
		$path = '@web/uploads/thumbnails/' . $url;
		
		return Html::img($path, array_merge(['alt' => $alt], $options));
	}
	
	/**
	* Validate and render IP address or fallback messages.
	* @param string|null $ip IP address string to validate and render.
	* @return string Safe HTML span or encoded IP address.
	* How to use:
	* 
	* 	[
	* 		'attribute' => 'ip',
	* 		'format' => 'raw',
	* 		'value' => function($model) => ViewHelper::renderIpAddress($model->ip),
	* 	],[
	* 		'attribute' => 'created_ip',
	* 		'format' => 'raw',
	* 		'value' => fn($model) => ViewHelper::renderIpAddress($model->created_ip),
	* 	],[
	* 		'attribute' => 'updated_ip',
	* 		'format' => 'raw',
	* 		'value' => fn($model) => ViewHelper::renderIpAddress($model->updated_ip),
	* 	],
	*/
	public static function renderIpAddress(?string $ip): string
	{
		$ip = trim((string) $ip);
		
		if ($ip === '')
		{
			return Html::tag('span', \Yii::t('app', '(not set)'), ['class' => 'not-set text-muted fst-italic']);
		}
		if (!filter_var($ip, FILTER_VALIDATE_IP))
		{
			return Html::tag('span', \Yii::t('app', '(invalid IP address)'), ['class' => 'invalid-ip']);
		}
		
		return Html::encode($ip);
	}
	
	//$formatter = Yii::$app->formatter;
	//$formatterHelper = FormatterHelper::create($formatter);
}
```

I want to expend my yii2 view helper with timedate, currency and time