@masterteam/properties 0.0.73 → 0.0.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -27,6 +27,11 @@ import { FormulaBuilder } from '@masterteam/formula-builder';
27
27
  import { SelectField } from '@masterteam/components/select-field';
28
28
  import { TextField } from '@masterteam/components/text-field';
29
29
  import { ToggleField } from '@masterteam/components/toggle-field';
30
+ import { ColorPickerField } from '@masterteam/components/color-picker-field';
31
+ import { NumberField } from '@masterteam/components/number-field';
32
+ import { SliderField } from '@masterteam/components/slider-field';
33
+ import { Progress } from '@masterteam/components/progress';
34
+ import { resolvePercentageColor } from '@masterteam/components/entities';
30
35
  import { ModalService } from '@masterteam/components/modal';
31
36
  import { ModalRef } from '@masterteam/components/dialog';
32
37
 
@@ -1899,39 +1904,455 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
1899
1904
  ], template: "<mt-dynamic-form formControlName=\"configuration\" [formConfig]=\"formConfig()\" />\r\n" }]
1900
1905
  }], ctorParameters: () => [] });
1901
1906
 
1907
+ /** Fallback colour applied when no rule matches. */
1908
+ const PERCENTAGE_DEFAULT_COLOR = '#293558';
1909
+ /**
1910
+ * The colour scale the backend applies to Percentage properties that carry no
1911
+ * custom configuration. A newly created property starts from these so the form
1912
+ * never has to submit `{}` or an empty `colorRules` array.
1913
+ */
1914
+ const PERCENTAGE_DEFAULT_COLOR_RULES = [
1915
+ { maxValue: 25, color: '#ed2517' },
1916
+ { maxValue: 50, color: '#FFB300' },
1917
+ { maxValue: 75, color: '#0A5EBE' },
1918
+ { maxValue: 100, color: '#4caf50' },
1919
+ ];
1920
+ const PERCENTAGE_MIN_VALUE = 0;
1921
+ const PERCENTAGE_MAX_VALUE = 100;
1922
+ const HEX_COLOR_PATTERN = /^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/;
1923
+ function toFiniteNumber(value) {
1924
+ if (value === null || value === undefined || value === '') {
1925
+ return null;
1926
+ }
1927
+ const parsed = typeof value === 'number' ? value : Number(value);
1928
+ return Number.isFinite(parsed) ? parsed : null;
1929
+ }
1930
+ function toColor(value, fallback) {
1931
+ const color = typeof value === 'string' ? value.trim() : '';
1932
+ return color || fallback;
1933
+ }
1934
+ function cloneDefaultRules() {
1935
+ return PERCENTAGE_DEFAULT_COLOR_RULES.map((rule) => ({ ...rule }));
1936
+ }
1937
+ /**
1938
+ * Reads whatever the backend stored into the draft the editor works on.
1939
+ *
1940
+ * Three inputs map to three different starting points, and the difference
1941
+ * matters: an absent configuration is a *new* property that should adopt the
1942
+ * default scale, while a legacy `{ color }` is an *existing* property whose
1943
+ * single colour must be preserved as-is — seeding it with default rules would
1944
+ * silently repaint it on the next save.
1945
+ */
1946
+ function normalizePercentageConfiguration(raw) {
1947
+ const source = raw && typeof raw === 'object' && !Array.isArray(raw)
1948
+ ? raw
1949
+ : null;
1950
+ if (!source || Object.keys(source).length === 0) {
1951
+ return {
1952
+ colorRules: cloneDefaultRules(),
1953
+ defaultColor: PERCENTAGE_DEFAULT_COLOR,
1954
+ };
1955
+ }
1956
+ const defaultColor = toColor(source['defaultColor'] ?? source['color'], PERCENTAGE_DEFAULT_COLOR);
1957
+ const rawRules = source['colorRules'];
1958
+ if (!Array.isArray(rawRules)) {
1959
+ // Legacy `{ color }`, or a shape with no rules at all: one flat colour.
1960
+ return { colorRules: [], defaultColor };
1961
+ }
1962
+ return {
1963
+ colorRules: rawRules
1964
+ .filter((rule) => !!rule && typeof rule === 'object')
1965
+ .map((rule) => ({
1966
+ minValue: toFiniteNumber(rule['minValue']),
1967
+ maxValue: toFiniteNumber(rule['maxValue']),
1968
+ color: toColor(rule['color'], PERCENTAGE_DEFAULT_COLOR),
1969
+ })),
1970
+ defaultColor,
1971
+ };
1972
+ }
1973
+ /**
1974
+ * Shapes the draft into what gets sent.
1975
+ *
1976
+ * Two payloads, never a third: with rules it is `{ colorRules, defaultColor }`,
1977
+ * without them it falls back to the legacy `{ color }`. `{}` and an empty
1978
+ * `colorRules` array are both rejected by the backend, so neither is reachable
1979
+ * from here.
1980
+ */
1981
+ function serializePercentageConfiguration(draft) {
1982
+ const defaultColor = toColor(draft.defaultColor, PERCENTAGE_DEFAULT_COLOR);
1983
+ const rules = draft.colorRules
1984
+ .filter((rule) => toFiniteNumber(rule.minValue) !== null ||
1985
+ toFiniteNumber(rule.maxValue) !== null)
1986
+ .map((rule) => {
1987
+ const min = toFiniteNumber(rule.minValue);
1988
+ const max = toFiniteNumber(rule.maxValue);
1989
+ return {
1990
+ // `minValue`/`maxValue` are both optional; only send the bounds the
1991
+ // rule actually declares rather than padding them with nulls.
1992
+ ...(min === null ? {} : { minValue: min }),
1993
+ ...(max === null ? {} : { maxValue: max }),
1994
+ color: toColor(rule.color, defaultColor),
1995
+ };
1996
+ });
1997
+ return rules.length
1998
+ ? { colorRules: rules, defaultColor }
1999
+ : { color: defaultColor };
2000
+ }
2001
+ function isEmptyBand(band) {
2002
+ if (band.from > band.to) {
2003
+ return true;
2004
+ }
2005
+ return band.from === band.to && (band.fromOpen || band.toOpen);
2006
+ }
2007
+ /** The part of `band` the closed rule range `[min, max]` claims. */
2008
+ function intersectRule(band, min, max) {
2009
+ return {
2010
+ from: Math.max(band.from, min),
2011
+ to: Math.min(band.to, max),
2012
+ // The rule range is closed on both ends, so only the band can contribute
2013
+ // an open boundary — and only where the band's own edge is the tighter one.
2014
+ fromOpen: min <= band.from && band.fromOpen,
2015
+ toOpen: max >= band.to && band.toOpen,
2016
+ };
2017
+ }
2018
+ /** `band` with everything inside `[min, max]` removed — 0, 1, or 2 pieces. */
2019
+ function subtractRule(band, min, max) {
2020
+ if (isEmptyBand(intersectRule(band, min, max))) {
2021
+ return [band];
2022
+ }
2023
+ const remainder = [
2024
+ { from: band.from, to: min, fromOpen: band.fromOpen, toOpen: true },
2025
+ { from: max, to: band.to, fromOpen: true, toOpen: band.toOpen },
2026
+ ];
2027
+ return remainder.filter((piece) => !isEmptyBand(piece));
2028
+ }
2029
+ /**
2030
+ * What the rule list actually covers, given that they are evaluated in order
2031
+ * and the first match wins. Both answers are advisory: a shadowed rule is dead
2032
+ * configuration, and a gap silently falls through to the default colour.
2033
+ */
2034
+ function analysePercentageRuleCoverage(rules) {
2035
+ let remaining = [
2036
+ {
2037
+ from: PERCENTAGE_MIN_VALUE,
2038
+ to: PERCENTAGE_MAX_VALUE,
2039
+ fromOpen: false,
2040
+ toOpen: false,
2041
+ },
2042
+ ];
2043
+ const unreachableRuleIndexes = [];
2044
+ rules.forEach((rule, index) => {
2045
+ const min = toFiniteNumber(rule.minValue) ?? PERCENTAGE_MIN_VALUE;
2046
+ const max = toFiniteNumber(rule.maxValue) ?? PERCENTAGE_MAX_VALUE;
2047
+ if (min > max) {
2048
+ // An inverted rule matches nothing, but that is already reported as a
2049
+ // validation error — don't pile a second warning on top of it.
2050
+ return;
2051
+ }
2052
+ const claimsSomething = remaining.some((band) => !isEmptyBand(intersectRule(band, min, max)));
2053
+ if (!claimsSomething) {
2054
+ unreachableRuleIndexes.push(index);
2055
+ return;
2056
+ }
2057
+ remaining = remaining.flatMap((band) => subtractRule(band, min, max));
2058
+ });
2059
+ return {
2060
+ unreachableRuleIndexes,
2061
+ gaps: remaining.map(({ from, to }) => ({ from, to })),
2062
+ };
2063
+ }
2064
+
2065
+ /** How finely the preview strip samples the 0–100 scale. */
2066
+ const PREVIEW_STEP = 0.5;
1902
2067
  class PercentageConfiguration {
1903
2068
  transloco = inject(TranslocoService);
1904
- formConfig = signal({
1905
- sections: [
1906
- {
1907
- key: 'configuration',
1908
- label: this.transloco.translate('properties.form.configurationSection'),
1909
- cssClass: ' rounded-xl bg-content \
1910
- shadow-sm \
1911
- px-6 py-4',
1912
- type: 'header',
1913
- bodyClass: 'grid grid-cols-1 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)] gap-3',
1914
- fields: [
1915
- {
1916
- key: 'color',
1917
- type: 'color-picker',
1918
- label: this.transloco.translate('properties.form.selectColor'),
1919
- },
2069
+ minValue = PERCENTAGE_MIN_VALUE;
2070
+ maxValue = PERCENTAGE_MAX_VALUE;
2071
+ form = new FormGroup({
2072
+ defaultColor: new FormControl(PERCENTAGE_DEFAULT_COLOR, {
2073
+ nonNullable: true,
2074
+ validators: [Validators.required, Validators.pattern(HEX_COLOR_PATTERN)],
2075
+ }),
2076
+ colorRules: new FormArray([]),
2077
+ });
2078
+ /** Preview-only; deliberately outside `form` so it never reaches the payload. */
2079
+ previewControl = new FormControl(60, { nonNullable: true });
2080
+ touched = false;
2081
+ onChange = () => { };
2082
+ onTouched = () => { };
2083
+ onValidatorChange = () => { };
2084
+ /**
2085
+ * Bumped on every form change *and* after `writeValue`, which rebuilds the
2086
+ * rule list silently — without this the preview would keep showing the rules
2087
+ * of the previously loaded property.
2088
+ */
2089
+ revision = signal(0, ...(ngDevMode ? [{ debugName: "revision" }] : /* istanbul ignore next */ []));
2090
+ previewValue = toSignal(this.previewControl.valueChanges, {
2091
+ initialValue: this.previewControl.value,
2092
+ });
2093
+ /** The current rule set, as plain data, recomputed on every form change. */
2094
+ draft = computed(() => {
2095
+ this.revision();
2096
+ return this.readDraft();
2097
+ }, ...(ngDevMode ? [{ debugName: "draft" }] : /* istanbul ignore next */ []));
2098
+ rules = computed(() => this.draft().colorRules, ...(ngDevMode ? [{ debugName: "rules" }] : /* istanbul ignore next */ []));
2099
+ hasRules = computed(() => this.rules().length > 0, ...(ngDevMode ? [{ debugName: "hasRules" }] : /* istanbul ignore next */ []));
2100
+ previewColor = computed(() => resolvePercentageColor(this.previewValue(), this.draft()) ?? 'primary', ...(ngDevMode ? [{ debugName: "previewColor" }] : /* istanbul ignore next */ []));
2101
+ /**
2102
+ * The 0–100 scale painted with the colour each value resolves to. Sampling
2103
+ * and then collapsing equal neighbours keeps this to a handful of DOM nodes
2104
+ * regardless of how the rules are written, and flex ordering keeps it correct
2105
+ * in RTL without a direction-aware gradient.
2106
+ */
2107
+ previewSegments = computed(() => {
2108
+ const draft = this.draft();
2109
+ const segments = [];
2110
+ for (let value = PERCENTAGE_MIN_VALUE; value <= PERCENTAGE_MAX_VALUE; value += PREVIEW_STEP) {
2111
+ const color = resolvePercentageColor(value, draft) ?? 'transparent';
2112
+ const last = segments[segments.length - 1];
2113
+ if (last && last.color === color) {
2114
+ last.weight += 1;
2115
+ }
2116
+ else {
2117
+ segments.push({ color, weight: 1 });
2118
+ }
2119
+ }
2120
+ return segments;
2121
+ }, ...(ngDevMode ? [{ debugName: "previewSegments" }] : /* istanbul ignore next */ []));
2122
+ coverage = computed(() => analysePercentageRuleCoverage(this.rules()), ...(ngDevMode ? [{ debugName: "coverage" }] : /* istanbul ignore next */ []));
2123
+ /**
2124
+ * Non-blocking hints about rules that can never fire and values that quietly
2125
+ * fall through to the default colour — both are easy to author by accident
2126
+ * once order decides the winner.
2127
+ */
2128
+ warnings = computed(() => {
2129
+ if (!this.hasRules()) {
2130
+ return [];
2131
+ }
2132
+ const { unreachableRuleIndexes, gaps } = this.coverage();
2133
+ return [
2134
+ ...unreachableRuleIndexes.map((index) => this.transloco.translate('properties.form.percentageWarningUnreachable', {
2135
+ index: index + 1,
2136
+ })),
2137
+ ...gaps.map((gap) => this.transloco.translate('properties.form.percentageWarningGap', {
2138
+ from: gap.from,
2139
+ to: gap.to,
2140
+ })),
2141
+ ];
2142
+ }, ...(ngDevMode ? [{ debugName: "warnings" }] : /* istanbul ignore next */ []));
2143
+ constructor() {
2144
+ this.form.valueChanges.pipe(takeUntilDestroyed()).subscribe(() => {
2145
+ this.revision.update((value) => value + 1);
2146
+ });
2147
+ // The value is pushed from an effect rather than straight from
2148
+ // `writeValue`/`valueChanges`, because Angular calls `writeValue` before
2149
+ // `registerOnChange` — anything emitted during setup would land on the
2150
+ // no-op callback and the property would save with no configuration at all.
2151
+ // By the time effects run the callback is registered.
2152
+ let initial = true;
2153
+ effect(() => {
2154
+ this.revision();
2155
+ this.emitValue(!initial);
2156
+ initial = false;
2157
+ this.onValidatorChange();
2158
+ });
2159
+ }
2160
+ get ruleControls() {
2161
+ return this.form.controls.colorRules.controls;
2162
+ }
2163
+ // ── ControlValueAccessor ──
2164
+ writeValue(value) {
2165
+ const draft = normalizePercentageConfiguration(value);
2166
+ this.form.controls.defaultColor.setValue(draft.defaultColor, {
2167
+ emitEvent: false,
2168
+ });
2169
+ this.setRules(draft.colorRules);
2170
+ this.touched = false;
2171
+ // Normalisation is lossy by design — an absent configuration becomes the
2172
+ // default scale, and a stored shape gets its bounds coerced — so the
2173
+ // control has to be told what the editor actually holds now. Bumping the
2174
+ // revision is what schedules that write, via the effect in the constructor.
2175
+ this.revision.update((current) => current + 1);
2176
+ }
2177
+ registerOnChange(fn) {
2178
+ this.onChange = fn;
2179
+ }
2180
+ registerOnTouched(fn) {
2181
+ this.onTouched = fn;
2182
+ }
2183
+ setDisabledState(isDisabled) {
2184
+ if (isDisabled) {
2185
+ this.form.disable({ emitEvent: false });
2186
+ this.previewControl.disable({ emitEvent: false });
2187
+ return;
2188
+ }
2189
+ this.form.enable({ emitEvent: false });
2190
+ this.previewControl.enable({ emitEvent: false });
2191
+ }
2192
+ // ── Validator ──
2193
+ validate(_) {
2194
+ // A disabled form reports DISABLED rather than VALID; it has nothing to
2195
+ // block the save with.
2196
+ if (this.form.disabled || this.form.valid) {
2197
+ return null;
2198
+ }
2199
+ return { percentageConfigurationInvalid: true };
2200
+ }
2201
+ registerOnValidatorChange(fn) {
2202
+ this.onValidatorChange = fn;
2203
+ }
2204
+ // ── Rule list ──
2205
+ addRule() {
2206
+ const rules = this.rules();
2207
+ const previous = rules[rules.length - 1];
2208
+ // Continue the scale rather than starting from a blank row: the next rule
2209
+ // almost always picks up where the previous one stopped.
2210
+ const previousMax = previous?.maxValue;
2211
+ const start = typeof previousMax === 'number' && previousMax < PERCENTAGE_MAX_VALUE
2212
+ ? previousMax + 1
2213
+ : null;
2214
+ this.form.controls.colorRules.push(this.createRuleGroup({
2215
+ minValue: start,
2216
+ maxValue: PERCENTAGE_MAX_VALUE,
2217
+ color: this.form.controls.defaultColor.value,
2218
+ }));
2219
+ this.markTouched();
2220
+ }
2221
+ removeRule(index) {
2222
+ this.form.controls.colorRules.removeAt(index);
2223
+ this.markTouched();
2224
+ }
2225
+ /** Moves a rule by `offset` positions — order is what decides the winner. */
2226
+ moveRule(index, offset) {
2227
+ const rules = this.form.controls.colorRules;
2228
+ const target = index + offset;
2229
+ if (target < 0 || target >= rules.length) {
2230
+ return;
2231
+ }
2232
+ const [moved] = rules.controls.splice(index, 1);
2233
+ rules.controls.splice(target, 0, moved);
2234
+ rules.updateValueAndValidity();
2235
+ this.markTouched();
2236
+ }
2237
+ restoreDefaults() {
2238
+ this.form.controls.defaultColor.setValue(PERCENTAGE_DEFAULT_COLOR, {
2239
+ emitEvent: false,
2240
+ });
2241
+ this.setRules(PERCENTAGE_DEFAULT_COLOR_RULES.map((rule) => ({ ...rule })), true);
2242
+ this.markTouched();
2243
+ }
2244
+ // ── Internals ──
2245
+ readDraft() {
2246
+ return {
2247
+ colorRules: this.ruleControls.map((group) => ({
2248
+ minValue: group.controls.minValue.value,
2249
+ maxValue: group.controls.maxValue.value,
2250
+ color: group.controls.color.value,
2251
+ })),
2252
+ defaultColor: this.form.controls.defaultColor.value,
2253
+ };
2254
+ }
2255
+ setRules(rules, emitEvent = false) {
2256
+ const array = this.form.controls.colorRules;
2257
+ array.clear({ emitEvent: false });
2258
+ rules.forEach((rule) => array.push(this.createRuleGroup(rule), { emitEvent: false }));
2259
+ array.updateValueAndValidity({ emitEvent });
2260
+ }
2261
+ createRuleGroup(rule) {
2262
+ const bounds = [
2263
+ Validators.min(PERCENTAGE_MIN_VALUE),
2264
+ Validators.max(PERCENTAGE_MAX_VALUE),
2265
+ ];
2266
+ return new FormGroup({
2267
+ minValue: new FormControl(rule.minValue ?? null, {
2268
+ validators: bounds,
2269
+ }),
2270
+ maxValue: new FormControl(rule.maxValue ?? null, {
2271
+ validators: bounds,
2272
+ }),
2273
+ color: new FormControl(rule.color ?? PERCENTAGE_DEFAULT_COLOR, {
2274
+ nonNullable: true,
2275
+ validators: [
2276
+ Validators.required,
2277
+ Validators.pattern(HEX_COLOR_PATTERN),
1920
2278
  ],
1921
- },
1922
- ],
1923
- }, ...(ngDevMode ? [{ debugName: "formConfig" }] : /* istanbul ignore next */ []));
2279
+ }),
2280
+ }, { validators: (group) => this.validateRuleBounds(group) });
2281
+ }
2282
+ /**
2283
+ * A rule needs at least one bound — with neither it would match nothing — and
2284
+ * an inverted pair is always a typo.
2285
+ */
2286
+ validateRuleBounds(group) {
2287
+ const min = group.get('minValue')?.value;
2288
+ const max = group.get('maxValue')?.value;
2289
+ if (min === null && max === null) {
2290
+ return {
2291
+ boundsRequired: {
2292
+ message: this.transloco.translate('properties.form.percentageRuleBoundsRequired'),
2293
+ },
2294
+ };
2295
+ }
2296
+ if (typeof min === 'number' && typeof max === 'number' && min > max) {
2297
+ return {
2298
+ boundsOrder: {
2299
+ message: this.transloco.translate('properties.form.percentageRuleBoundsOrder'),
2300
+ },
2301
+ };
2302
+ }
2303
+ return null;
2304
+ }
2305
+ emitValue(triggerTouched = true) {
2306
+ this.onChange(serializePercentageConfiguration(this.readDraft()));
2307
+ if (triggerTouched && !this.touched) {
2308
+ this.onTouched();
2309
+ this.touched = true;
2310
+ }
2311
+ }
2312
+ markTouched() {
2313
+ this.form.markAsDirty();
2314
+ if (!this.touched) {
2315
+ this.onTouched();
2316
+ this.touched = true;
2317
+ }
2318
+ }
1924
2319
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: PercentageConfiguration, deps: [], target: i0.ɵɵFactoryTarget.Component });
1925
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: PercentageConfiguration, isStandalone: true, selector: "mt-percentage-configuration", ngImport: i0, template: "<mt-dynamic-form formControlName=\"configuration\" [formConfig]=\"formConfig()\" />\r\n", styles: [""], dependencies: [{ kind: "component", type: DynamicForm, selector: "mt-dynamic-form", inputs: ["formConfig", "forcedHiddenFieldKeys", "forcedDisabledFieldKeys", "preserveForcedHiddenValues", "visibleSectionKeys", "externalValues"], outputs: ["runtimeMessagesChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }], viewProviders: [
1926
- { provide: ControlContainer, useExisting: FormGroupDirective },
1927
- ] });
2320
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: PercentageConfiguration, isStandalone: true, selector: "mt-percentage-configuration", providers: [
2321
+ {
2322
+ provide: NG_VALUE_ACCESSOR,
2323
+ useExisting: forwardRef(() => PercentageConfiguration),
2324
+ multi: true,
2325
+ },
2326
+ {
2327
+ provide: NG_VALIDATORS,
2328
+ useExisting: forwardRef(() => PercentageConfiguration),
2329
+ multi: true,
2330
+ },
2331
+ ], ngImport: i0, template: "<div\r\n [formGroup]=\"form\"\r\n class=\"space-y-4 rounded-xl bg-content px-6 py-4 shadow-sm\"\r\n>\r\n <div class=\"flex flex-wrap items-start justify-between gap-3\">\r\n <div class=\"space-y-1\">\r\n <h3 class=\"text-base font-semibold\">\r\n {{ \"properties.form.percentageColorsSection\" | transloco }}\r\n </h3>\r\n <p class=\"max-w-prose text-xs text-surface-500\">\r\n {{ \"properties.form.percentageColorsHint\" | transloco }}\r\n </p>\r\n </div>\r\n <div class=\"flex shrink-0 items-center gap-2\">\r\n <mt-button\r\n type=\"button\"\r\n [text]=\"true\"\r\n icon=\"arrow.refresh-ccw-01\"\r\n [label]=\"'properties.form.percentageRestoreDefaults' | transloco\"\r\n (click)=\"restoreDefaults()\"\r\n />\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.plus\"\r\n [label]=\"'properties.form.percentageAddRule' | transloco\"\r\n (click)=\"addRule()\"\r\n />\r\n </div>\r\n </div>\r\n\r\n @if (ruleControls.length) {\r\n <div class=\"space-y-2\">\r\n @for (ruleCtrl of ruleControls; track ruleCtrl; let i = $index) {\r\n <div\r\n [formGroup]=\"ruleCtrl\"\r\n class=\"rounded-lg border border-surface-200 px-3 py-2\"\r\n >\r\n <div\r\n class=\"grid items-start gap-3 md:grid-cols-[auto_minmax(0,7rem)_minmax(0,7rem)_minmax(0,1fr)_auto]\"\r\n >\r\n <span\r\n class=\"mt-2 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-surface-100 text-xs font-semibold text-surface-600\"\r\n [title]=\"'properties.form.percentageRuleOrderHint' | transloco\"\r\n >\r\n {{ i + 1 }}\r\n </span>\r\n <mt-number-field\r\n formControlName=\"minValue\"\r\n [min]=\"minValue\"\r\n [max]=\"maxValue\"\r\n [label]=\"'properties.form.percentageRuleFrom' | transloco\"\r\n [placeholder]=\"'properties.form.percentageRuleOpen' | transloco\"\r\n />\r\n <mt-number-field\r\n formControlName=\"maxValue\"\r\n [min]=\"minValue\"\r\n [max]=\"maxValue\"\r\n [label]=\"'properties.form.percentageRuleTo' | transloco\"\r\n [placeholder]=\"'properties.form.percentageRuleOpen' | transloco\"\r\n />\r\n <mt-color-picker-field\r\n formControlName=\"color\"\r\n [label]=\"'properties.form.percentageRuleColor' | transloco\"\r\n />\r\n <div class=\"flex items-center gap-1 md:mt-6\">\r\n <mt-button\r\n type=\"button\"\r\n [text]=\"true\"\r\n icon=\"arrow.chevron-up\"\r\n [disabled]=\"i === 0\"\r\n [tooltip]=\"'properties.form.percentageMoveUp' | transloco\"\r\n (click)=\"moveRule(i, -1)\"\r\n />\r\n <mt-button\r\n type=\"button\"\r\n [text]=\"true\"\r\n icon=\"arrow.chevron-down\"\r\n [disabled]=\"i === ruleControls.length - 1\"\r\n [tooltip]=\"'properties.form.percentageMoveDown' | transloco\"\r\n (click)=\"moveRule(i, 1)\"\r\n />\r\n <mt-button\r\n type=\"button\"\r\n [text]=\"true\"\r\n severity=\"danger\"\r\n icon=\"general.trash-01\"\r\n [tooltip]=\"'properties.form.percentageRemoveRule' | transloco\"\r\n (click)=\"removeRule(i)\"\r\n />\r\n </div>\r\n </div>\r\n @if (ruleCtrl.errors && ruleCtrl.touched) {\r\n <p class=\"text-xs text-red-500\">\r\n {{\r\n ruleCtrl.errors[\"boundsRequired\"]?.message ??\r\n ruleCtrl.errors[\"boundsOrder\"]?.message\r\n }}\r\n </p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n <p\r\n class=\"rounded-lg border border-dashed border-surface-300 px-4 py-3 text-xs text-surface-500\"\r\n >\r\n {{ \"properties.form.percentageNoRules\" | transloco }}\r\n </p>\r\n }\r\n\r\n <div class=\"grid gap-4 md:grid-cols-[minmax(0,18rem)_minmax(0,1fr)]\">\r\n <div class=\"space-y-1\">\r\n <mt-color-picker-field\r\n formControlName=\"defaultColor\"\r\n [label]=\"'properties.form.percentageDefaultColor' | transloco\"\r\n />\r\n <p class=\"text-xs text-surface-400\">\r\n {{ \"properties.form.percentageDefaultColorHint\" | transloco }}\r\n </p>\r\n </div>\r\n\r\n <div class=\"space-y-2\">\r\n <span class=\"text-xs font-medium text-surface-500\">\r\n {{ \"properties.form.percentagePreview\" | transloco }}\r\n </span>\r\n <div class=\"flex h-3 overflow-hidden rounded-full\">\r\n @for (segment of previewSegments(); track $index) {\r\n <span\r\n class=\"block h-full\"\r\n [style.flex-grow]=\"segment.weight\"\r\n [style.background-color]=\"segment.color\"\r\n ></span>\r\n }\r\n </div>\r\n <div class=\"flex justify-between text-[10px] text-surface-400\">\r\n <span>{{ minValue }}%</span>\r\n <span>{{ maxValue }}%</span>\r\n </div>\r\n <div class=\"grid items-center gap-3 md:grid-cols-[minmax(0,1fr)_9rem]\">\r\n <mt-slider-field\r\n [formControl]=\"previewControl\"\r\n [min]=\"minValue\"\r\n [max]=\"maxValue\"\r\n [label]=\"'properties.form.percentagePreviewValue' | transloco\"\r\n />\r\n <mt-progress\r\n [value]=\"previewControl.value\"\r\n [color]=\"previewColor()\"\r\n [height]=\"9\"\r\n [showLabel]=\"false\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n @if (warnings().length) {\r\n <ul class=\"space-y-1 text-xs text-amber-600\">\r\n @for (warning of warnings(); track warning) {\r\n <li>{{ warning }}</li>\r\n }\r\n </ul>\r\n }\r\n</div>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: ColorPickerField, selector: "mt-color-picker-field", inputs: ["label", "appendTo", "placeholder", "class", "variant", "readonly", "pInputs", "required"], outputs: ["onChange"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: SliderField, selector: "mt-slider-field", inputs: ["field", "label", "class", "min", "max", "step", "hideNumber", "unit", "readonly", "required"] }, { kind: "component", type: Progress, selector: "mt-progress", inputs: ["value", "mode", "showLabel", "unit", "color", "minValue", "maxValue", "height", "circleSize", "strokeWidth", "customClass"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1928
2332
  }
1929
2333
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: PercentageConfiguration, decorators: [{
1930
2334
  type: Component,
1931
- args: [{ selector: 'mt-percentage-configuration', imports: [DynamicForm, ReactiveFormsModule], viewProviders: [
1932
- { provide: ControlContainer, useExisting: FormGroupDirective },
1933
- ], template: "<mt-dynamic-form formControlName=\"configuration\" [formConfig]=\"formConfig()\" />\r\n" }]
1934
- }] });
2335
+ args: [{ selector: 'mt-percentage-configuration', standalone: true, imports: [
2336
+ ReactiveFormsModule,
2337
+ TranslocoModule,
2338
+ Button,
2339
+ ColorPickerField,
2340
+ NumberField,
2341
+ SliderField,
2342
+ Progress,
2343
+ ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
2344
+ {
2345
+ provide: NG_VALUE_ACCESSOR,
2346
+ useExisting: forwardRef(() => PercentageConfiguration),
2347
+ multi: true,
2348
+ },
2349
+ {
2350
+ provide: NG_VALIDATORS,
2351
+ useExisting: forwardRef(() => PercentageConfiguration),
2352
+ multi: true,
2353
+ },
2354
+ ], template: "<div\r\n [formGroup]=\"form\"\r\n class=\"space-y-4 rounded-xl bg-content px-6 py-4 shadow-sm\"\r\n>\r\n <div class=\"flex flex-wrap items-start justify-between gap-3\">\r\n <div class=\"space-y-1\">\r\n <h3 class=\"text-base font-semibold\">\r\n {{ \"properties.form.percentageColorsSection\" | transloco }}\r\n </h3>\r\n <p class=\"max-w-prose text-xs text-surface-500\">\r\n {{ \"properties.form.percentageColorsHint\" | transloco }}\r\n </p>\r\n </div>\r\n <div class=\"flex shrink-0 items-center gap-2\">\r\n <mt-button\r\n type=\"button\"\r\n [text]=\"true\"\r\n icon=\"arrow.refresh-ccw-01\"\r\n [label]=\"'properties.form.percentageRestoreDefaults' | transloco\"\r\n (click)=\"restoreDefaults()\"\r\n />\r\n <mt-button\r\n type=\"button\"\r\n icon=\"general.plus\"\r\n [label]=\"'properties.form.percentageAddRule' | transloco\"\r\n (click)=\"addRule()\"\r\n />\r\n </div>\r\n </div>\r\n\r\n @if (ruleControls.length) {\r\n <div class=\"space-y-2\">\r\n @for (ruleCtrl of ruleControls; track ruleCtrl; let i = $index) {\r\n <div\r\n [formGroup]=\"ruleCtrl\"\r\n class=\"rounded-lg border border-surface-200 px-3 py-2\"\r\n >\r\n <div\r\n class=\"grid items-start gap-3 md:grid-cols-[auto_minmax(0,7rem)_minmax(0,7rem)_minmax(0,1fr)_auto]\"\r\n >\r\n <span\r\n class=\"mt-2 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-surface-100 text-xs font-semibold text-surface-600\"\r\n [title]=\"'properties.form.percentageRuleOrderHint' | transloco\"\r\n >\r\n {{ i + 1 }}\r\n </span>\r\n <mt-number-field\r\n formControlName=\"minValue\"\r\n [min]=\"minValue\"\r\n [max]=\"maxValue\"\r\n [label]=\"'properties.form.percentageRuleFrom' | transloco\"\r\n [placeholder]=\"'properties.form.percentageRuleOpen' | transloco\"\r\n />\r\n <mt-number-field\r\n formControlName=\"maxValue\"\r\n [min]=\"minValue\"\r\n [max]=\"maxValue\"\r\n [label]=\"'properties.form.percentageRuleTo' | transloco\"\r\n [placeholder]=\"'properties.form.percentageRuleOpen' | transloco\"\r\n />\r\n <mt-color-picker-field\r\n formControlName=\"color\"\r\n [label]=\"'properties.form.percentageRuleColor' | transloco\"\r\n />\r\n <div class=\"flex items-center gap-1 md:mt-6\">\r\n <mt-button\r\n type=\"button\"\r\n [text]=\"true\"\r\n icon=\"arrow.chevron-up\"\r\n [disabled]=\"i === 0\"\r\n [tooltip]=\"'properties.form.percentageMoveUp' | transloco\"\r\n (click)=\"moveRule(i, -1)\"\r\n />\r\n <mt-button\r\n type=\"button\"\r\n [text]=\"true\"\r\n icon=\"arrow.chevron-down\"\r\n [disabled]=\"i === ruleControls.length - 1\"\r\n [tooltip]=\"'properties.form.percentageMoveDown' | transloco\"\r\n (click)=\"moveRule(i, 1)\"\r\n />\r\n <mt-button\r\n type=\"button\"\r\n [text]=\"true\"\r\n severity=\"danger\"\r\n icon=\"general.trash-01\"\r\n [tooltip]=\"'properties.form.percentageRemoveRule' | transloco\"\r\n (click)=\"removeRule(i)\"\r\n />\r\n </div>\r\n </div>\r\n @if (ruleCtrl.errors && ruleCtrl.touched) {\r\n <p class=\"text-xs text-red-500\">\r\n {{\r\n ruleCtrl.errors[\"boundsRequired\"]?.message ??\r\n ruleCtrl.errors[\"boundsOrder\"]?.message\r\n }}\r\n </p>\r\n }\r\n </div>\r\n }\r\n </div>\r\n } @else {\r\n <p\r\n class=\"rounded-lg border border-dashed border-surface-300 px-4 py-3 text-xs text-surface-500\"\r\n >\r\n {{ \"properties.form.percentageNoRules\" | transloco }}\r\n </p>\r\n }\r\n\r\n <div class=\"grid gap-4 md:grid-cols-[minmax(0,18rem)_minmax(0,1fr)]\">\r\n <div class=\"space-y-1\">\r\n <mt-color-picker-field\r\n formControlName=\"defaultColor\"\r\n [label]=\"'properties.form.percentageDefaultColor' | transloco\"\r\n />\r\n <p class=\"text-xs text-surface-400\">\r\n {{ \"properties.form.percentageDefaultColorHint\" | transloco }}\r\n </p>\r\n </div>\r\n\r\n <div class=\"space-y-2\">\r\n <span class=\"text-xs font-medium text-surface-500\">\r\n {{ \"properties.form.percentagePreview\" | transloco }}\r\n </span>\r\n <div class=\"flex h-3 overflow-hidden rounded-full\">\r\n @for (segment of previewSegments(); track $index) {\r\n <span\r\n class=\"block h-full\"\r\n [style.flex-grow]=\"segment.weight\"\r\n [style.background-color]=\"segment.color\"\r\n ></span>\r\n }\r\n </div>\r\n <div class=\"flex justify-between text-[10px] text-surface-400\">\r\n <span>{{ minValue }}%</span>\r\n <span>{{ maxValue }}%</span>\r\n </div>\r\n <div class=\"grid items-center gap-3 md:grid-cols-[minmax(0,1fr)_9rem]\">\r\n <mt-slider-field\r\n [formControl]=\"previewControl\"\r\n [min]=\"minValue\"\r\n [max]=\"maxValue\"\r\n [label]=\"'properties.form.percentagePreviewValue' | transloco\"\r\n />\r\n <mt-progress\r\n [value]=\"previewControl.value\"\r\n [color]=\"previewColor()\"\r\n [height]=\"9\"\r\n [showLabel]=\"false\"\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n\r\n @if (warnings().length) {\r\n <ul class=\"space-y-1 text-xs text-amber-600\">\r\n @for (warning of warnings(); track warning) {\r\n <li>{{ warning }}</li>\r\n }\r\n </ul>\r\n }\r\n</div>\r\n" }]
2355
+ }], ctorParameters: () => [] });
1935
2356
 
1936
2357
  class StatusItemForm {
1937
2358
  modal = inject(ModalService);
@@ -2966,12 +3387,22 @@ class PropertyForm {
2966
3387
  const isTranslatable = supportsTranslatableToggle
2967
3388
  ? Boolean(mainValue.isTranslatable)
2968
3389
  : Boolean(this.facade.selected()?.isTranslatable);
3390
+ // On edit `mainValue` is the whole loaded property, so it still carries the
3391
+ // stored lowercase `configuration`. The backend matches JSON keys
3392
+ // case-insensitively, so shipping it alongside `Configuration` leaves which
3393
+ // one binds up to key order — pull it out and send a single canonical key,
3394
+ // still falling back to the stored value for the view types that have no
3395
+ // configuration editor at all.
3396
+ const { configuration: loadedConfiguration, ...mainPayload } = mainValue;
2969
3397
  const payload = {
2970
- ...mainValue,
3398
+ ...mainPayload,
2971
3399
  isCalculated,
2972
3400
  isTranslatable,
2973
3401
  formula: isCalculated ? this.formulaControl.value : null,
2974
- Configuration: this.configurationControl.value ?? mainValue.Configuration ?? null,
3402
+ Configuration: this.configurationControl.value ??
3403
+ mainValue.Configuration ??
3404
+ loadedConfiguration ??
3405
+ null,
2975
3406
  };
2976
3407
  // On edit, never prompt: any addToForm value loaded into the form passes
2977
3408
  // through via the spread above so the backend flag stays untouched.
@@ -3091,7 +3522,7 @@ class PropertyForm {
3091
3522
  this.subscriptions.unsubscribe();
3092
3523
  }
3093
3524
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: PropertyForm, deps: [], target: i0.ɵɵFactoryTarget.Component });
3094
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: PropertyForm, isStandalone: true, selector: "mt-property-form", inputs: { propertyId: { classPropertyName: "propertyId", publicName: "propertyId", isSignal: true, isRequired: false, transformFunction: null }, scope: { classPropertyName: "scope", publicName: "scope", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "statusConfigurationSection", first: true, predicate: ["statusConfigurationSection"], descendants: true, isSignal: true }, { propertyName: "configurationHost", first: true, predicate: ["configurationHost"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "<mt-page\r\n [title]=\"\r\n propertyId()\r\n ? ('properties.form.editProperty' | transloco)\r\n : ('properties.form.createNewProperty' | transloco)\r\n \"\r\n avatarIcon=\"custom.products-and-services\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-sky-50)',\r\n '--p-avatar-color': 'var(--p-sky-700)',\r\n }\"\r\n (backButtonClick)=\"goBack()\"\r\n backButton\r\n class=\"h-full\"\r\n>\r\n <ng-template #headerEnd>\r\n <mt-button\r\n class=\"mx-2\"\r\n [label]=\"submitLabel()\"\r\n [icon]=\"isEditing() ? 'custom.pencil' : 'general.plus'\"\r\n [loading]=\"submitting()\"\r\n [disabled]=\"submitDisabled() || this.propertyForm.invalid\"\r\n (click)=\"createOrEditProperty($event)\"\r\n />\r\n </ng-template>\r\n <div\r\n [formGroup]=\"propertyForm\"\r\n class=\"h-full py-4 h-full overflow-y-auto flex justify-center\"\r\n >\r\n <div class=\"w-2/3 flex flex-col gap-6\">\r\n @if (loading()) {\r\n <!-- Skeleton Loading State -->\r\n <div class=\"rounded-xl bg-white shadow-sm p-6 space-y-4\">\r\n <div class=\"flex justify-between items-center gap-6\">\r\n <p-skeleton width=\"50%\" height=\"3rem\"></p-skeleton>\r\n <p-skeleton width=\"8rem\" height=\"2.5rem\"></p-skeleton>\r\n </div>\r\n </div>\r\n\r\n <div class=\"rounded-xl bg-white shadow-sm p-6 space-y-4\">\r\n <p-skeleton\r\n width=\"12rem\"\r\n height=\"1.5rem\"\r\n styleClass=\"mb-4\"\r\n ></p-skeleton>\r\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"6rem\" styleClass=\"md:col-span-2\"></p-skeleton>\r\n <p-skeleton height=\"3rem\" styleClass=\"md:col-span-2\"></p-skeleton>\r\n </div>\r\n </div>\r\n\r\n <div class=\"rounded-xl bg-white shadow-sm p-6 space-y-4\">\r\n <p-skeleton\r\n width=\"10rem\"\r\n height=\"1.5rem\"\r\n styleClass=\"mb-4\"\r\n ></p-skeleton>\r\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"8rem\" styleClass=\"md:col-span-2\"></p-skeleton>\r\n </div>\r\n </div>\r\n } @else {\r\n <mt-dynamic-form\r\n [formConfig]=\"dynamicFormConfigMain()\"\r\n [forcedDisabledFieldKeys]=\"lockedMainFieldKeys()\"\r\n [formControlName]=\"'main'\"\r\n />\r\n @if (configurationFormConfig()) {\r\n <mt-dynamic-form\r\n formControlName=\"configuration\"\r\n [formConfig]=\"configurationFormConfig()!\"\r\n />\r\n } @else {\r\n <ng-container #configurationHost></ng-container>\r\n @if (!configurationComponentExists()) {\r\n @switch (propertyType()) {\r\n @case (\"User\") {\r\n <mt-user-configuration />\r\n }\r\n @case (\"Percentage\") {\r\n <mt-percentage-configuration />\r\n }\r\n @case (\"Lookup\") {\r\n <mt-lookup-configuration />\r\n }\r\n @case (\"LookupMultiSelect\") {\r\n <mt-lookup-configuration />\r\n }\r\n @case (\"EntityList\") {\r\n <mt-entity-list-configuration />\r\n }\r\n @case (\"API\") {\r\n <mt-api-configuration formControlName=\"configuration\" />\r\n }\r\n @case (\"Status\") {\r\n <section #statusConfigurationSection>\r\n <mt-status-configuration\r\n [propertyId]=\"statusPropertyId()\"\r\n [contextKey]=\"formulaContextKey()\"\r\n [levelSchemaId]=\"formulaSchemaId()\"\r\n [moduleId]=\"formulaModuleId()\"\r\n />\r\n </section>\r\n }\r\n <!-- @case('ViewList') { REMOVED FOR NOW\r\n <mt-view-list-configuration />\r\n } -->\r\n @case (\"Attachment\") {\r\n <mt-attachment-configuration />\r\n }\r\n <!-- @case('ReferenceProperty') { REMOVED FOR NOW\r\n } -->\r\n @case (\"LookupModuleCheckList\") {\r\n <mt-check-list-form-configuration />\r\n }\r\n <!-- @case('LookupMatrix') { REMOVED FOR NOW\r\n <mt-lookup-configuration />\r\n } -->\r\n @case (\"Location\") {\r\n <mt-location-configuration />\r\n }\r\n }\r\n }\r\n }\r\n @if (isCalculated() && !hideFormulaBuilder()) {\r\n <mt-card>\r\n <ng-template #headless>\r\n <div\r\n class=\"flex items-center justify-between px-4 py-5 border-b border-surface\"\r\n >\r\n <h3 class=\"text-xl font-semibold\">\r\n {{ \"properties.form.formula\" | transloco }}\r\n <span class=\"text-red-500\">*</span>\r\n </h3>\r\n </div>\r\n <div class=\"flex-1 min-h-0 p-4\">\r\n <mt-formula-builder\r\n formControlName=\"formula\"\r\n [contextKey]=\"formulaContextKey()\"\r\n [levelSchemaId]=\"formulaSchemaId()\"\r\n [moduleId]=\"formulaModuleId()\"\r\n />\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n }\r\n }\r\n </div>\r\n </div>\r\n</mt-page>\r\n", styles: [""], dependencies: [{ kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: DynamicForm, selector: "mt-dynamic-form", inputs: ["formConfig", "forcedHiddenFieldKeys", "forcedDisabledFieldKeys", "preserveForcedHiddenValues", "visibleSectionKeys", "externalValues"], outputs: ["runtimeMessagesChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: FormulaBuilder, selector: "mt-formula-builder", inputs: ["propertiesByPath", "levelSchemaId", "moduleId", "contextKey", "contextEntityTypeKey", "templateId", "placeholder", "hideToolbar", "hideStatusBar", "toolbarTabs", "codeOnly", "builderOnly", "valueMode", "isProcessBuilder"], outputs: ["validationChange", "tokensChange"] }, { kind: "component", type: ApiConfiguration, selector: "mt-api-configuration" }, { kind: "component", type: CheckListFormConfiguration, selector: "mt-check-list-form-configuration" }, { kind: "component", type: EntityListConfiguration, selector: "mt-entity-list-configuration" }, { kind: "component", type: LocationConfiguration, selector: "mt-location-configuration" }, { kind: "component", type: LookupConfiguration, selector: "mt-lookup-configuration" }, { kind: "component", type: PercentageConfiguration, selector: "mt-percentage-configuration" }, { kind: "component", type: StatusConfiguration, selector: "mt-status-configuration", inputs: ["propertyId", "contextKey", "levelSchemaId", "moduleId"] }, { kind: "component", type: UserConfiguration, selector: "mt-user-configuration" }, { kind: "component", type: AttachmentConfiguration, selector: "mt-attachment-configuration" }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i2$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
3525
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: PropertyForm, isStandalone: true, selector: "mt-property-form", inputs: { propertyId: { classPropertyName: "propertyId", publicName: "propertyId", isSignal: true, isRequired: false, transformFunction: null }, scope: { classPropertyName: "scope", publicName: "scope", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "statusConfigurationSection", first: true, predicate: ["statusConfigurationSection"], descendants: true, isSignal: true }, { propertyName: "configurationHost", first: true, predicate: ["configurationHost"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "<mt-page\r\n [title]=\"\r\n propertyId()\r\n ? ('properties.form.editProperty' | transloco)\r\n : ('properties.form.createNewProperty' | transloco)\r\n \"\r\n avatarIcon=\"custom.products-and-services\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-sky-50)',\r\n '--p-avatar-color': 'var(--p-sky-700)',\r\n }\"\r\n (backButtonClick)=\"goBack()\"\r\n backButton\r\n class=\"h-full\"\r\n>\r\n <ng-template #headerEnd>\r\n <mt-button\r\n class=\"mx-2\"\r\n [label]=\"submitLabel()\"\r\n [icon]=\"isEditing() ? 'custom.pencil' : 'general.plus'\"\r\n [loading]=\"submitting()\"\r\n [disabled]=\"submitDisabled() || this.propertyForm.invalid\"\r\n (click)=\"createOrEditProperty($event)\"\r\n />\r\n </ng-template>\r\n <div\r\n [formGroup]=\"propertyForm\"\r\n class=\"h-full py-4 h-full overflow-y-auto flex justify-center\"\r\n >\r\n <div class=\"w-2/3 flex flex-col gap-6\">\r\n @if (loading()) {\r\n <!-- Skeleton Loading State -->\r\n <div class=\"rounded-xl bg-white shadow-sm p-6 space-y-4\">\r\n <div class=\"flex justify-between items-center gap-6\">\r\n <p-skeleton width=\"50%\" height=\"3rem\"></p-skeleton>\r\n <p-skeleton width=\"8rem\" height=\"2.5rem\"></p-skeleton>\r\n </div>\r\n </div>\r\n\r\n <div class=\"rounded-xl bg-white shadow-sm p-6 space-y-4\">\r\n <p-skeleton\r\n width=\"12rem\"\r\n height=\"1.5rem\"\r\n styleClass=\"mb-4\"\r\n ></p-skeleton>\r\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"6rem\" styleClass=\"md:col-span-2\"></p-skeleton>\r\n <p-skeleton height=\"3rem\" styleClass=\"md:col-span-2\"></p-skeleton>\r\n </div>\r\n </div>\r\n\r\n <div class=\"rounded-xl bg-white shadow-sm p-6 space-y-4\">\r\n <p-skeleton\r\n width=\"10rem\"\r\n height=\"1.5rem\"\r\n styleClass=\"mb-4\"\r\n ></p-skeleton>\r\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"8rem\" styleClass=\"md:col-span-2\"></p-skeleton>\r\n </div>\r\n </div>\r\n } @else {\r\n <mt-dynamic-form\r\n [formConfig]=\"dynamicFormConfigMain()\"\r\n [forcedDisabledFieldKeys]=\"lockedMainFieldKeys()\"\r\n [formControlName]=\"'main'\"\r\n />\r\n @if (configurationFormConfig()) {\r\n <mt-dynamic-form\r\n formControlName=\"configuration\"\r\n [formConfig]=\"configurationFormConfig()!\"\r\n />\r\n } @else {\r\n <ng-container #configurationHost></ng-container>\r\n @if (!configurationComponentExists()) {\r\n @switch (propertyType()) {\r\n @case (\"User\") {\r\n <mt-user-configuration />\r\n }\r\n @case (\"Percentage\") {\r\n <mt-percentage-configuration formControlName=\"configuration\" />\r\n }\r\n @case (\"Lookup\") {\r\n <mt-lookup-configuration />\r\n }\r\n @case (\"LookupMultiSelect\") {\r\n <mt-lookup-configuration />\r\n }\r\n @case (\"EntityList\") {\r\n <mt-entity-list-configuration />\r\n }\r\n @case (\"API\") {\r\n <mt-api-configuration formControlName=\"configuration\" />\r\n }\r\n @case (\"Status\") {\r\n <section #statusConfigurationSection>\r\n <mt-status-configuration\r\n [propertyId]=\"statusPropertyId()\"\r\n [contextKey]=\"formulaContextKey()\"\r\n [levelSchemaId]=\"formulaSchemaId()\"\r\n [moduleId]=\"formulaModuleId()\"\r\n />\r\n </section>\r\n }\r\n <!-- @case('ViewList') { REMOVED FOR NOW\r\n <mt-view-list-configuration />\r\n } -->\r\n @case (\"Attachment\") {\r\n <mt-attachment-configuration />\r\n }\r\n <!-- @case('ReferenceProperty') { REMOVED FOR NOW\r\n } -->\r\n @case (\"LookupModuleCheckList\") {\r\n <mt-check-list-form-configuration />\r\n }\r\n <!-- @case('LookupMatrix') { REMOVED FOR NOW\r\n <mt-lookup-configuration />\r\n } -->\r\n @case (\"Location\") {\r\n <mt-location-configuration />\r\n }\r\n }\r\n }\r\n }\r\n @if (isCalculated() && !hideFormulaBuilder()) {\r\n <mt-card>\r\n <ng-template #headless>\r\n <div\r\n class=\"flex items-center justify-between px-4 py-5 border-b border-surface\"\r\n >\r\n <h3 class=\"text-xl font-semibold\">\r\n {{ \"properties.form.formula\" | transloco }}\r\n <span class=\"text-red-500\">*</span>\r\n </h3>\r\n </div>\r\n <div class=\"flex-1 min-h-0 p-4\">\r\n <mt-formula-builder\r\n formControlName=\"formula\"\r\n [contextKey]=\"formulaContextKey()\"\r\n [levelSchemaId]=\"formulaSchemaId()\"\r\n [moduleId]=\"formulaModuleId()\"\r\n />\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n }\r\n }\r\n </div>\r\n </div>\r\n</mt-page>\r\n", styles: [""], dependencies: [{ kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: DynamicForm, selector: "mt-dynamic-form", inputs: ["formConfig", "forcedHiddenFieldKeys", "forcedDisabledFieldKeys", "preserveForcedHiddenValues", "visibleSectionKeys", "externalValues"], outputs: ["runtimeMessagesChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: FormulaBuilder, selector: "mt-formula-builder", inputs: ["propertiesByPath", "levelSchemaId", "moduleId", "contextKey", "contextEntityTypeKey", "templateId", "placeholder", "hideToolbar", "hideStatusBar", "toolbarTabs", "codeOnly", "builderOnly", "valueMode", "isProcessBuilder"], outputs: ["validationChange", "tokensChange"] }, { kind: "component", type: ApiConfiguration, selector: "mt-api-configuration" }, { kind: "component", type: CheckListFormConfiguration, selector: "mt-check-list-form-configuration" }, { kind: "component", type: EntityListConfiguration, selector: "mt-entity-list-configuration" }, { kind: "component", type: LocationConfiguration, selector: "mt-location-configuration" }, { kind: "component", type: LookupConfiguration, selector: "mt-lookup-configuration" }, { kind: "component", type: PercentageConfiguration, selector: "mt-percentage-configuration" }, { kind: "component", type: StatusConfiguration, selector: "mt-status-configuration", inputs: ["propertyId", "contextKey", "levelSchemaId", "moduleId"] }, { kind: "component", type: UserConfiguration, selector: "mt-user-configuration" }, { kind: "component", type: AttachmentConfiguration, selector: "mt-attachment-configuration" }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i2$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
3095
3526
  }
3096
3527
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: PropertyForm, decorators: [{
3097
3528
  type: Component,
@@ -3113,7 +3544,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
3113
3544
  AttachmentConfiguration,
3114
3545
  SkeletonModule,
3115
3546
  TranslocoModule,
3116
- ], template: "<mt-page\r\n [title]=\"\r\n propertyId()\r\n ? ('properties.form.editProperty' | transloco)\r\n : ('properties.form.createNewProperty' | transloco)\r\n \"\r\n avatarIcon=\"custom.products-and-services\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-sky-50)',\r\n '--p-avatar-color': 'var(--p-sky-700)',\r\n }\"\r\n (backButtonClick)=\"goBack()\"\r\n backButton\r\n class=\"h-full\"\r\n>\r\n <ng-template #headerEnd>\r\n <mt-button\r\n class=\"mx-2\"\r\n [label]=\"submitLabel()\"\r\n [icon]=\"isEditing() ? 'custom.pencil' : 'general.plus'\"\r\n [loading]=\"submitting()\"\r\n [disabled]=\"submitDisabled() || this.propertyForm.invalid\"\r\n (click)=\"createOrEditProperty($event)\"\r\n />\r\n </ng-template>\r\n <div\r\n [formGroup]=\"propertyForm\"\r\n class=\"h-full py-4 h-full overflow-y-auto flex justify-center\"\r\n >\r\n <div class=\"w-2/3 flex flex-col gap-6\">\r\n @if (loading()) {\r\n <!-- Skeleton Loading State -->\r\n <div class=\"rounded-xl bg-white shadow-sm p-6 space-y-4\">\r\n <div class=\"flex justify-between items-center gap-6\">\r\n <p-skeleton width=\"50%\" height=\"3rem\"></p-skeleton>\r\n <p-skeleton width=\"8rem\" height=\"2.5rem\"></p-skeleton>\r\n </div>\r\n </div>\r\n\r\n <div class=\"rounded-xl bg-white shadow-sm p-6 space-y-4\">\r\n <p-skeleton\r\n width=\"12rem\"\r\n height=\"1.5rem\"\r\n styleClass=\"mb-4\"\r\n ></p-skeleton>\r\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"6rem\" styleClass=\"md:col-span-2\"></p-skeleton>\r\n <p-skeleton height=\"3rem\" styleClass=\"md:col-span-2\"></p-skeleton>\r\n </div>\r\n </div>\r\n\r\n <div class=\"rounded-xl bg-white shadow-sm p-6 space-y-4\">\r\n <p-skeleton\r\n width=\"10rem\"\r\n height=\"1.5rem\"\r\n styleClass=\"mb-4\"\r\n ></p-skeleton>\r\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"8rem\" styleClass=\"md:col-span-2\"></p-skeleton>\r\n </div>\r\n </div>\r\n } @else {\r\n <mt-dynamic-form\r\n [formConfig]=\"dynamicFormConfigMain()\"\r\n [forcedDisabledFieldKeys]=\"lockedMainFieldKeys()\"\r\n [formControlName]=\"'main'\"\r\n />\r\n @if (configurationFormConfig()) {\r\n <mt-dynamic-form\r\n formControlName=\"configuration\"\r\n [formConfig]=\"configurationFormConfig()!\"\r\n />\r\n } @else {\r\n <ng-container #configurationHost></ng-container>\r\n @if (!configurationComponentExists()) {\r\n @switch (propertyType()) {\r\n @case (\"User\") {\r\n <mt-user-configuration />\r\n }\r\n @case (\"Percentage\") {\r\n <mt-percentage-configuration />\r\n }\r\n @case (\"Lookup\") {\r\n <mt-lookup-configuration />\r\n }\r\n @case (\"LookupMultiSelect\") {\r\n <mt-lookup-configuration />\r\n }\r\n @case (\"EntityList\") {\r\n <mt-entity-list-configuration />\r\n }\r\n @case (\"API\") {\r\n <mt-api-configuration formControlName=\"configuration\" />\r\n }\r\n @case (\"Status\") {\r\n <section #statusConfigurationSection>\r\n <mt-status-configuration\r\n [propertyId]=\"statusPropertyId()\"\r\n [contextKey]=\"formulaContextKey()\"\r\n [levelSchemaId]=\"formulaSchemaId()\"\r\n [moduleId]=\"formulaModuleId()\"\r\n />\r\n </section>\r\n }\r\n <!-- @case('ViewList') { REMOVED FOR NOW\r\n <mt-view-list-configuration />\r\n } -->\r\n @case (\"Attachment\") {\r\n <mt-attachment-configuration />\r\n }\r\n <!-- @case('ReferenceProperty') { REMOVED FOR NOW\r\n } -->\r\n @case (\"LookupModuleCheckList\") {\r\n <mt-check-list-form-configuration />\r\n }\r\n <!-- @case('LookupMatrix') { REMOVED FOR NOW\r\n <mt-lookup-configuration />\r\n } -->\r\n @case (\"Location\") {\r\n <mt-location-configuration />\r\n }\r\n }\r\n }\r\n }\r\n @if (isCalculated() && !hideFormulaBuilder()) {\r\n <mt-card>\r\n <ng-template #headless>\r\n <div\r\n class=\"flex items-center justify-between px-4 py-5 border-b border-surface\"\r\n >\r\n <h3 class=\"text-xl font-semibold\">\r\n {{ \"properties.form.formula\" | transloco }}\r\n <span class=\"text-red-500\">*</span>\r\n </h3>\r\n </div>\r\n <div class=\"flex-1 min-h-0 p-4\">\r\n <mt-formula-builder\r\n formControlName=\"formula\"\r\n [contextKey]=\"formulaContextKey()\"\r\n [levelSchemaId]=\"formulaSchemaId()\"\r\n [moduleId]=\"formulaModuleId()\"\r\n />\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n }\r\n }\r\n </div>\r\n </div>\r\n</mt-page>\r\n" }]
3547
+ ], template: "<mt-page\r\n [title]=\"\r\n propertyId()\r\n ? ('properties.form.editProperty' | transloco)\r\n : ('properties.form.createNewProperty' | transloco)\r\n \"\r\n avatarIcon=\"custom.products-and-services\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-sky-50)',\r\n '--p-avatar-color': 'var(--p-sky-700)',\r\n }\"\r\n (backButtonClick)=\"goBack()\"\r\n backButton\r\n class=\"h-full\"\r\n>\r\n <ng-template #headerEnd>\r\n <mt-button\r\n class=\"mx-2\"\r\n [label]=\"submitLabel()\"\r\n [icon]=\"isEditing() ? 'custom.pencil' : 'general.plus'\"\r\n [loading]=\"submitting()\"\r\n [disabled]=\"submitDisabled() || this.propertyForm.invalid\"\r\n (click)=\"createOrEditProperty($event)\"\r\n />\r\n </ng-template>\r\n <div\r\n [formGroup]=\"propertyForm\"\r\n class=\"h-full py-4 h-full overflow-y-auto flex justify-center\"\r\n >\r\n <div class=\"w-2/3 flex flex-col gap-6\">\r\n @if (loading()) {\r\n <!-- Skeleton Loading State -->\r\n <div class=\"rounded-xl bg-white shadow-sm p-6 space-y-4\">\r\n <div class=\"flex justify-between items-center gap-6\">\r\n <p-skeleton width=\"50%\" height=\"3rem\"></p-skeleton>\r\n <p-skeleton width=\"8rem\" height=\"2.5rem\"></p-skeleton>\r\n </div>\r\n </div>\r\n\r\n <div class=\"rounded-xl bg-white shadow-sm p-6 space-y-4\">\r\n <p-skeleton\r\n width=\"12rem\"\r\n height=\"1.5rem\"\r\n styleClass=\"mb-4\"\r\n ></p-skeleton>\r\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"6rem\" styleClass=\"md:col-span-2\"></p-skeleton>\r\n <p-skeleton height=\"3rem\" styleClass=\"md:col-span-2\"></p-skeleton>\r\n </div>\r\n </div>\r\n\r\n <div class=\"rounded-xl bg-white shadow-sm p-6 space-y-4\">\r\n <p-skeleton\r\n width=\"10rem\"\r\n height=\"1.5rem\"\r\n styleClass=\"mb-4\"\r\n ></p-skeleton>\r\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"3rem\"></p-skeleton>\r\n <p-skeleton height=\"8rem\" styleClass=\"md:col-span-2\"></p-skeleton>\r\n </div>\r\n </div>\r\n } @else {\r\n <mt-dynamic-form\r\n [formConfig]=\"dynamicFormConfigMain()\"\r\n [forcedDisabledFieldKeys]=\"lockedMainFieldKeys()\"\r\n [formControlName]=\"'main'\"\r\n />\r\n @if (configurationFormConfig()) {\r\n <mt-dynamic-form\r\n formControlName=\"configuration\"\r\n [formConfig]=\"configurationFormConfig()!\"\r\n />\r\n } @else {\r\n <ng-container #configurationHost></ng-container>\r\n @if (!configurationComponentExists()) {\r\n @switch (propertyType()) {\r\n @case (\"User\") {\r\n <mt-user-configuration />\r\n }\r\n @case (\"Percentage\") {\r\n <mt-percentage-configuration formControlName=\"configuration\" />\r\n }\r\n @case (\"Lookup\") {\r\n <mt-lookup-configuration />\r\n }\r\n @case (\"LookupMultiSelect\") {\r\n <mt-lookup-configuration />\r\n }\r\n @case (\"EntityList\") {\r\n <mt-entity-list-configuration />\r\n }\r\n @case (\"API\") {\r\n <mt-api-configuration formControlName=\"configuration\" />\r\n }\r\n @case (\"Status\") {\r\n <section #statusConfigurationSection>\r\n <mt-status-configuration\r\n [propertyId]=\"statusPropertyId()\"\r\n [contextKey]=\"formulaContextKey()\"\r\n [levelSchemaId]=\"formulaSchemaId()\"\r\n [moduleId]=\"formulaModuleId()\"\r\n />\r\n </section>\r\n }\r\n <!-- @case('ViewList') { REMOVED FOR NOW\r\n <mt-view-list-configuration />\r\n } -->\r\n @case (\"Attachment\") {\r\n <mt-attachment-configuration />\r\n }\r\n <!-- @case('ReferenceProperty') { REMOVED FOR NOW\r\n } -->\r\n @case (\"LookupModuleCheckList\") {\r\n <mt-check-list-form-configuration />\r\n }\r\n <!-- @case('LookupMatrix') { REMOVED FOR NOW\r\n <mt-lookup-configuration />\r\n } -->\r\n @case (\"Location\") {\r\n <mt-location-configuration />\r\n }\r\n }\r\n }\r\n }\r\n @if (isCalculated() && !hideFormulaBuilder()) {\r\n <mt-card>\r\n <ng-template #headless>\r\n <div\r\n class=\"flex items-center justify-between px-4 py-5 border-b border-surface\"\r\n >\r\n <h3 class=\"text-xl font-semibold\">\r\n {{ \"properties.form.formula\" | transloco }}\r\n <span class=\"text-red-500\">*</span>\r\n </h3>\r\n </div>\r\n <div class=\"flex-1 min-h-0 p-4\">\r\n <mt-formula-builder\r\n formControlName=\"formula\"\r\n [contextKey]=\"formulaContextKey()\"\r\n [levelSchemaId]=\"formulaSchemaId()\"\r\n [moduleId]=\"formulaModuleId()\"\r\n />\r\n </div>\r\n </ng-template>\r\n </mt-card>\r\n }\r\n }\r\n </div>\r\n </div>\r\n</mt-page>\r\n" }]
3117
3548
  }], ctorParameters: () => [], propDecorators: { propertyId: [{ type: i0.Input, args: [{ isSignal: true, alias: "propertyId", required: false }] }], scope: [{ type: i0.Input, args: [{ isSignal: true, alias: "scope", required: false }] }], configurationHost: [{
3118
3549
  type: ViewChild,
3119
3550
  args: ['configurationHost', { read: ViewContainerRef }]