Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.00% covered (success)
92.00%
23 / 25
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
FieldUiConfiguration
95.83% covered (success)
95.83%
23 / 24
75.00% covered (warning)
75.00%
3 / 4
19
0.00% covered (danger)
0.00%
0 / 1
 resolveIconClass
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 resolveHeuristicIcon
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
5.07
 resolveFormDisplayMode
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
 resolveDisplayFormat
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Core\Engine\Domain\Model\Support;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11/**
12 * Field UI Configuration Helper.
13 *
14 * Encapsulates UI presentation rules, icons, display modes, and form visibility heuristics for FieldMetadata.
15 *
16 * @package App\Core\Engine\Domain\Model\Support
17 */
18final readonly class FieldUiConfiguration
19{
20    private const string ICON_PALETTE = 'bi bi-palette';
21    private const string ICON_CARD_LIST = 'bi bi-card-list';
22    private const string ICON_ACTIVITY = 'bi bi-activity';
23    private const string ICON_TAGS = 'bi bi-tags';
24
25    /** @var array<string, string> Default icon mapping per UiType. */
26    public const array UITYPE_ICONS = [
27        'string_input'   => 'bi bi-type',
28        'text_area'      => 'bi bi-text-paragraph',
29        'email_input'    => 'bi bi-envelope',
30        'ip_address'     => 'bi bi-hdd-network',
31        'integer_number' => 'bi bi-hash',
32        'bigint_number'  => 'bi bi-hash',
33        'decimal_number' => 'bi bi-cash-stack',
34        'currency_amount'=> 'bi bi-currency-dollar',
35        'select'         => self::ICON_CARD_LIST,
36        'single_select'  => self::ICON_CARD_LIST,
37        'picklist'       => self::ICON_CARD_LIST,
38        'boolean'        => 'bi bi-toggle-on',
39        'toggle'         => 'bi bi-toggle-on',
40        'date'           => 'bi bi-calendar3',
41        'datetime'       => 'bi bi-calendar-event',
42        'user_reference' => 'bi bi-person-badge',
43        'module_reference'=> 'bi bi-boxes',
44        'relation_1m_picklist'=> 'bi bi-bookmark-star',
45        '1M_picklist'    => 'bi bi-bookmark-star',
46        'password'       => 'bi bi-key',
47        'color'          => self::ICON_PALETTE,
48        'icon_picker'    => self::ICON_PALETTE,
49        'url'            => 'bi bi-link-45deg',
50        'phone'          => 'bi bi-telephone',
51        'autonumber'     => 'bi bi-123',
52        'prefix_number'  => 'bi bi-123',
53        'favorite'       => 'bi bi-star',
54        'pin'            => 'bi bi-pin-angle',
55    ];
56
57    /** @var array<string, string> Default icon mapping per field key heuristics. */
58    public const array FIELD_KEY_ICONS = [
59        'subject'           => 'bi bi-chat-left-text',
60        'event_type'        => 'bi bi-grid',
61        'status'            => self::ICON_ACTIVITY,
62        'order_status'      => self::ICON_ACTIVITY,
63        'quote_status'      => self::ICON_ACTIVITY,
64        'ticket_status'     => self::ICON_ACTIVITY,
65        'product_status'    => self::ICON_ACTIVITY,
66        'service_status'    => self::ICON_ACTIVITY,
67        'category'          => self::ICON_TAGS,
68        'priority'          => 'bi bi-exclamation-triangle',
69        'color'             => self::ICON_PALETTE,
70        'body_html'         => self::ICON_PALETTE,
71        'start_date'        => 'bi bi-calendar-event',
72        'end_date'          => 'bi bi-calendar-check',
73        'is_all_day'        => 'bi bi-sun',
74        'c_isopaque'        => 'bi bi-slash-circle',
75        'reminder_minutes'  => 'bi bi-bell',
76        'location'          => 'bi bi-geo-alt',
77        'meeting_url'       => 'bi bi-camera-video',
78        'description'       => 'bi bi-text-paragraph',
79        'parent_id'         => 'bi bi-diagram-2',
80        'related_party_ref' => 'bi bi-building',
81        'process_ref'       => 'bi bi-folder2-open',
82        'subprocess_ref'    => 'bi bi-check2-square',
83        'attendees'         => 'bi bi-people',
84        'recurrence_rule'   => 'bi bi-arrow-repeat',
85        'c_uid'             => 'bi bi-fingerprint',
86        'c_etag'            => 'bi bi-shield-check',
87        'sequence'          => 'bi bi-sort-numeric-down',
88    ];
89
90    /** @var list<string> Common field keys that serve as primary record titles. */
91    public const array TITLE_FIELD_KEYS = [
92        'name', 'username', 'title',
93        'message_key', 'subject', 'heading', 'contract_name', 'ticket_no', 'contract_no',
94        'project_name', 'product_name', 'service_name', 'sold_product_name', 'sold_service_name',
95        'stage_name', 'task_name', 'mailbox_name', 'identifier', 'last_name',
96    ];
97
98    /**
99     * Resolves icon class for field.
100     *
101     * @param string               $fieldKey        Field machine key.
102     * @param string               $uitypeName      UiType name.
103     * @param array<string, mixed> $validationRules Validation rules.
104     * @return string CSS icon class.
105     */
106    public function resolveIconClass(string $fieldKey, string $uitypeName, array $validationRules): string
107    {
108        if (isset($validationRules['icon_class']) && is_string($validationRules['icon_class'])) {
109            return $validationRules['icon_class'];
110        }
111
112        $heuristic = $this->resolveHeuristicIcon($fieldKey);
113        if ($heuristic !== null) {
114            return $heuristic;
115        }
116
117        return self::UITYPE_ICONS[$uitypeName] ?? 'bi bi-tag';
118    }
119
120    private function resolveHeuristicIcon(string $fieldKey): ?string
121    {
122        if (isset(self::FIELD_KEY_ICONS[$fieldKey])) {
123            return self::FIELD_KEY_ICONS[$fieldKey];
124        }
125
126        if (str_ends_with($fieldKey, '_status')) {
127            return self::ICON_ACTIVITY;
128        }
129
130        return (str_ends_with($fieldKey, '_category') || str_contains($fieldKey, 'category'))
131            ? self::ICON_TAGS
132            : null;
133    }
134
135    /**
136     * Resolves form display mode.
137     *
138     * @param array<string, mixed> $validationRules Validation rules.
139     * @return string Display mode string.
140     */
141    public function resolveFormDisplayMode(array $validationRules): string
142    {
143        if (isset($validationRules['form_display_mode']) && is_string($validationRules['form_display_mode'])) {
144            return $validationRules['form_display_mode'];
145        }
146
147        if (isset($validationRules['display_mode']) && is_string($validationRules['display_mode'])) {
148            return $validationRules['display_mode'];
149        }
150
151        return 'icon_label_value';
152    }
153
154    /**
155     * Resolves date/time display format string.
156     *
157     * @param string               $uitypeName      UiType machine name.
158     * @param array<string, mixed> $validationRules Validation rules dictionary.
159     * @return string Format key.
160     */
161    public function resolveDisplayFormat(string $uitypeName, array $validationRules): string
162    {
163        $custom = $validationRules['display_format'] ?? $validationRules['date_format'] ?? null;
164        if (is_string($custom) && $custom !== '') {
165            return $custom;
166        }
167
168        if ($uitypeName === 'date' || $uitypeName === 'date_picker') {
169            return 'date_only';
170        }
171
172        return 'datetime_seconds';
173    }
174}