Skip to content

Commit 3a6e723

Browse files
Merge pull request #1293 from rocket-admin/fix/dark-theme-gauge-and-sort-widgets
gauge, sort widgets, various improvements
2 parents 468e920 + bfb6878 commit 3a6e723

33 files changed

Lines changed: 711 additions & 149 deletions

backend/src/enums/widget-type.enum.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ export enum WidgetTypeEnum {
1919
Code = 'Code',
2020
Phone = 'Phone',
2121
Country = 'Country',
22-
Color = 'Color'
22+
Color = 'Color',
23+
Range = 'Range'
2324
}

frontend/src/app/components/dashboard/db-table-widgets/db-table-widgets.component.ts

Lines changed: 106 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -95,132 +95,158 @@ export class DbTableWidgetsComponent implements OnInit {
9595
"allow_null": false
9696
}
9797
}`,
98-
Date: `// No settings required`,
99-
Default: `// No settings required`,
100-
Time: `// No settings required`,
101-
DateTime: `// No settings required`,
102-
JSON: `// No settings required`,
103-
Textarea: `// provide number of strings to show.
104-
{
105-
"rows": 5
106-
}`,
107-
String: `// No settings required`,
108-
Readonly: `// No settings required`,
109-
Number: `// Configure number display with unit conversion
110-
// Example units: "bytes", "meters", "seconds", "grams"
98+
Code:
99+
`// provide language of code to highlight: 'html', 'css', 'typescript', 'yaml', 'markdown'
100+
// example:
111101
{
112-
"unit": null
113-
}`,
114-
Select:
115-
`// provide array of options to map database value (key 'value') in human readable value (key 'label');
116-
// for example:
117-
// AK => Alaska,
118-
// CA => California
102+
"language": "html"
103+
}
104+
`,
105+
Color: `// Optional: Specify output format for color values
106+
// Supported formats: "hex", "hex_hash" (default), "rgb", "hsl"
107+
// Example configuration:
119108
120109
{
121-
"allow_null": true,
122-
"options": [
123-
{
124-
"value": "UA",
125-
"label": "🇺🇦 Ukraine"
126-
},
127-
{
128-
"value": "PL",
129-
"label": "🇵🇱 Poland"
130-
},
131-
{
132-
"value": "US",
133-
"label": "🇺🇸 United States"
134-
}
135-
]
136-
}`,
137-
Password:
138-
`// provide algorithm to encrypt your password, one of:
139-
//sha1, sha3, sha224, sha256, sha512, sha384, bcrypt, scrypt, argon2, pbkdf2.
140-
// example:
110+
"format": "hex_hash" // Will display colors as "#FF5733"
111+
}
141112
113+
// Format options:
114+
// - "hex": Display as "FF5733" (no hash)
115+
// - "hex_hash": Display as "#FF5733" (default)
116+
// - "rgb": Display as "rgb(255, 87, 51)"
117+
// - "hsl": Display as "hsl(9, 100%, 60%)"`,
118+
Country: `// Configure country display options
119+
// Example:
142120
{
143-
"encrypt": true,
144-
"algorithm": "sha256"
121+
"show_flag": true,
122+
"allow_null": false
145123
}
146-
147124
`,
125+
Date: `// Configure date display options
126+
// formatDistanceWithinHours: Shows relative time (e.g., "2 hours ago") for dates within the specified hours
127+
// Default: 48 hours. Set to 0 to disable relative time display
128+
{
129+
"formatDistanceWithinHours": 48
130+
}`,
131+
DateTime: `// Configure datetime display options
132+
// formatDistanceWithinHours: Shows relative time (e.g., "2 hours ago") for dates within the specified hours
133+
// Default: 48 hours. Set to 0 to disable relative time display
134+
{
135+
"formatDistanceWithinHours": 48
136+
}`,
137+
Default: `// No settings required`,
148138
File:
149139
`// provide type of file: 'hex', 'base64' or 'file'
150140
// example:
151141
{
152142
"type": "hex"
153143
}
154144
`,
155-
Code:
156-
`// provide language of code to highlight: 'html', 'css', 'typescript', 'yaml', 'markdown'
157-
// example:
145+
Foreign_key: `// Provide settings for foreign key widget
158146
{
159-
"language": "html"
147+
"column_name": "", // copy the name of the column you selected
148+
"referenced_column_name": "",
149+
"referenced_table_name": ""
160150
}
161151
`,
162152
Image:
163153
`// provide image height in px to dispaly in table
154+
// prefix: optional URL prefix to prepend to image source
164155
// example:
165156
{
166-
"height": 100
157+
"height": 100,
158+
"prefix": "https://example.com/images/"
159+
}
167160
}
168161
`,
169-
URL: `// No settings required`,
170-
Phone:
171-
`// Configure international phone number widget
162+
JSON: `// No settings required`,
163+
Money: `// Configure money widget settings
172164
// example:
173165
{
174-
"preferred_countries": ["US", "GB", "CA"],
175-
"enable_placeholder": true,
176-
"phone_validation": true
166+
"default_currency": "USD",
167+
"show_currency_selector": false,
168+
"decimal_places": 2,
169+
"allow_negative": true
177170
}
178171
`,
179-
Country: `// Configure country display options
180-
// Example:
172+
Number: `// Configure number display with unit conversion
173+
// Example units: "bytes", "meters", "seconds", "grams"
181174
{
182-
"show_flag": true,
183-
"allow_null": false
175+
"unit": null
176+
}`,
177+
Password:
178+
`// provide algorithm to encrypt your password, one of:
179+
//sha1, sha3, sha224, sha256, sha512, sha384, bcrypt, scrypt, argon2, pbkdf2.
180+
// example:
181+
182+
{
183+
"encrypt": true,
184+
"algorithm": "sha256"
184185
}
186+
185187
`,
186-
Foreign_key: `// Provide settings for foreign key widget
188+
Phone:
189+
`// Configure international phone number widget
190+
// example:
187191
{
188-
"column_name": "", // copy the name of the column you selected
189-
"referenced_column_name": "",
190-
"referenced_table_name": ""
192+
"preferred_countries": ["US", "GB", "CA"],
193+
"enable_placeholder": true,
194+
"phone_validation": true
191195
}
192196
`,
193-
Money: `// Configure money widget settings
194-
// example:
197+
Range: `// Configure the minimum, maximum and step values for the range
198+
// Default: min = 0, max = 100, step = 1
195199
{
196-
"default_currency": "USD",
197-
"show_currency_selector": false,
198-
"decimal_places": 2,
199-
"allow_negative": true
200+
"min": 0,
201+
"max": 100,
202+
"step": 1
200203
}
201204
`,
202-
Color: `// Optional: Specify output format for color values
203-
// Supported formats: "hex", "hex_hash" (default), "rgb", "hsl"
204-
// Example configuration:
205+
Readonly: `// No settings required`,
206+
Select:
207+
`// provide array of options to map database value (key 'value') in human readable value (key 'label');
208+
// for example:
209+
// AK => Alaska,
210+
// CA => California
205211
206212
{
207-
"format": "hex_hash" // Will display colors as "#FF5733"
213+
"allow_null": true,
214+
"options": [
215+
{
216+
"value": "UA",
217+
"label": "🇺🇦 Ukraine"
218+
},
219+
{
220+
"value": "PL",
221+
"label": "🇵🇱 Poland"
222+
},
223+
{
224+
"value": "US",
225+
"label": "🇺🇸 United States"
226+
}
227+
]
228+
}`,
229+
String: `// No settings required`,
230+
Textarea: `// provide number of strings to show.
231+
{
232+
"rows": 5
233+
}`,
234+
Time: `// No settings required`,
235+
URL: `// prefix: optional URL prefix to prepend to the href
236+
// example:
237+
{
238+
"prefix": "https://example.com/"
208239
}
209-
210-
// Format options:
211-
// - "hex": Display as "FF5733" (no hash)
212-
// - "hex_hash": Display as "#FF5733" (default)
213-
// - "rgb": Display as "rgb(255, 87, 51)"
214-
// - "hsl": Display as "hsl(9, 100%, 60%)"`,
215-
UUID: `// Configure UUID generation version and parameters
240+
`,
241+
UUID: `// Configure UUID generation version and parameters
216242
// Available versions: "v1", "v3", "v4" (default), "v5", "v7"
217243
// For v3/v5: provide namespace and optionally name
218244
{
219245
"version": "v4",
220246
"namespace": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
221247
"name": ""
222248
}
223-
`
249+
`,
224250
}
225251

226252
constructor(

frontend/src/app/components/ui-components/record-edit-fields/country/country.component.css

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,25 @@
22
width: 100%;
33
}
44

5+
.country-input-container {
6+
display: flex;
7+
align-items: center;
8+
width: 100%;
9+
}
10+
11+
.country-flag-prefix {
12+
font-size: 20px;
13+
margin-right: 8px;
14+
line-height: 1;
15+
}
16+
17+
.country-input {
18+
flex: 1;
19+
border: none !important;
20+
outline: none !important;
21+
background: transparent !important;
22+
}
23+
524
.country-flag {
625
margin-right: 8px;
726
font-size: 16px;

frontend/src/app/components/ui-components/record-edit-fields/country/country.component.html

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
<mat-form-field class="country-form-field" appearance="outline">
22
<mat-label>{{normalizedLabel}}</mat-label>
3-
<input type="text" matInput
4-
[required]="required" [disabled]="disabled" [readonly]="readonly"
5-
attr.data-testid="record-{{label}}-country"
6-
[formControl]="countryControl"
7-
[matAutocomplete]="auto">
3+
<div class="country-input-container">
4+
<span *ngIf="selectedCountryFlag && showFlag" class="country-flag-prefix">{{selectedCountryFlag}}</span>
5+
<input type="text" matInput
6+
[required]="required" [disabled]="disabled" [readonly]="readonly"
7+
attr.data-testid="record-{{label}}-country"
8+
[formControl]="countryControl"
9+
[matAutocomplete]="auto"
10+
class="country-input">
11+
</div>
812
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
913
<mat-option *ngFor="let country of filteredCountries | async"
1014
[value]="country"

frontend/src/app/components/ui-components/record-edit-fields/country/country.component.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export class CountryEditComponent extends BaseEditFieldComponent {
2626
public countryControl = new FormControl<{value: string | null, label: string, flag: string} | string>('');
2727
public filteredCountries: Observable<{value: string | null, label: string, flag: string}[]>;
2828
public showFlag: boolean = true;
29+
public selectedCountryFlag: string = '';
2930

3031
originalOrder = () => { return 0; }
3132

@@ -58,7 +59,16 @@ export class CountryEditComponent extends BaseEditFieldComponent {
5859
private setupAutocomplete(): void {
5960
this.filteredCountries = this.countryControl.valueChanges.pipe(
6061
startWith(''),
61-
map(value => this._filter(typeof value === 'string' ? value : (value?.label || '')))
62+
map(value => {
63+
// Update flag when value changes
64+
if (typeof value === 'object' && value !== null) {
65+
this.selectedCountryFlag = value.flag;
66+
} else if (typeof value === 'string') {
67+
// Clear flag if user is typing
68+
this.selectedCountryFlag = '';
69+
}
70+
return this._filter(typeof value === 'string' ? value : (value?.label || ''));
71+
})
6272
);
6373
}
6474

@@ -67,6 +77,7 @@ export class CountryEditComponent extends BaseEditFieldComponent {
6777
const country = this.countries.find(c => c.value === this.value);
6878
if (country) {
6979
this.countryControl.setValue(country);
80+
this.selectedCountryFlag = country.flag;
7081
}
7182
}
7283
}
@@ -81,19 +92,22 @@ export class CountryEditComponent extends BaseEditFieldComponent {
8192

8293
onCountrySelected(selectedCountry: {value: string | null, label: string, flag: string}): void {
8394
this.value = selectedCountry.value;
95+
this.selectedCountryFlag = selectedCountry.flag;
8496
this.onFieldChange.emit(this.value);
8597
}
8698

8799
displayFn(country: any): string {
88-
return country ? country.label : '';
100+
if (!country) return '';
101+
// Only return the country label, flag is shown separately
102+
return typeof country === 'string' ? country : country.label;
89103
}
90104

91105
private loadCountries(): void {
92106
this.countries = COUNTRIES.map(country => ({
93107
value: country.code,
94108
label: country.name,
95109
flag: getCountryFlag(country.code)
96-
}));
110+
})).toSorted((a, b) => a.label.localeCompare(b.label));
97111

98112
if (this.widgetStructure?.widget_params?.allow_null || this.structure?.allow_null) {
99113
this.countries = [{ value: null, label: '', flag: '' }, ...this.countries];
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
<div class="image-box">
22
<mat-form-field class="image-box__url" appearance="outline">
33
<mat-label>{{normalizedLabel}}</mat-label>
4+
<span *ngIf="prefix" matTextPrefix>{{prefix}}</span>
45
<input matInput type="text" name="{{label}}-{{key}}" #image="ngModel"
56
[required]="required" [disabled]="disabled" [readonly]="readonly"
67
urlValidator
8+
[urlPrefix]="prefix"
79
attr.data-testid="record-{{label}}-image"
810
[(ngModel)]="value" (ngModelChange)="onFieldChange.emit($event)">
911
<mat-error *ngIf="image.errors?.isInvalidURL">URL is invalid.</mat-error>
1012
</mat-form-field>
11-
<img *ngIf="!image.errors?.isInvalidURL" [src]="value" class="image-box__image">
13+
<img *ngIf="!image.errors?.isInvalidURL" [src]="imageUrl" class="image-box__image">
1214
</div>

0 commit comments

Comments
 (0)