@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
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
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
|
-
}
|
|
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: "
|
|
1926
|
-
{
|
|
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',
|
|
1932
|
-
|
|
1933
|
-
|
|
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
|
-
...
|
|
3398
|
+
...mainPayload,
|
|
2971
3399
|
isCalculated,
|
|
2972
3400
|
isTranslatable,
|
|
2973
3401
|
formula: isCalculated ? this.formulaControl.value : null,
|
|
2974
|
-
Configuration: this.configurationControl.value ??
|
|
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 }]
|