Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion backend/src/enums/widget-type.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ export enum WidgetTypeEnum {
Code = 'Code',
Phone = 'Phone',
Country = 'Country',
Color = 'Color'
Color = 'Color',
Range = 'Range'
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,132 +95,158 @@ export class DbTableWidgetsComponent implements OnInit {
"allow_null": false
}
}`,
Date: `// No settings required`,
Default: `// No settings required`,
Time: `// No settings required`,
DateTime: `// No settings required`,
JSON: `// No settings required`,
Textarea: `// provide number of strings to show.
{
"rows": 5
}`,
String: `// No settings required`,
Readonly: `// No settings required`,
Number: `// Configure number display with unit conversion
// Example units: "bytes", "meters", "seconds", "grams"
Code:
`// provide language of code to highlight: 'html', 'css', 'typescript', 'yaml', 'markdown'
// example:
{
"unit": null
}`,
Select:
`// provide array of options to map database value (key 'value') in human readable value (key 'label');
// for example:
// AK => Alaska,
// CA => California
"language": "html"
}
`,
Color: `// Optional: Specify output format for color values
// Supported formats: "hex", "hex_hash" (default), "rgb", "hsl"
// Example configuration:

{
"allow_null": true,
"options": [
{
"value": "UA",
"label": "🇺🇦 Ukraine"
},
{
"value": "PL",
"label": "🇵🇱 Poland"
},
{
"value": "US",
"label": "🇺🇸 United States"
}
]
}`,
Password:
`// provide algorithm to encrypt your password, one of:
//sha1, sha3, sha224, sha256, sha512, sha384, bcrypt, scrypt, argon2, pbkdf2.
// example:
"format": "hex_hash" // Will display colors as "#FF5733"
}

// Format options:
// - "hex": Display as "FF5733" (no hash)
// - "hex_hash": Display as "#FF5733" (default)
// - "rgb": Display as "rgb(255, 87, 51)"
// - "hsl": Display as "hsl(9, 100%, 60%)"`,
Country: `// Configure country display options
// Example:
{
"encrypt": true,
"algorithm": "sha256"
"show_flag": true,
"allow_null": false
}

`,
Date: `// Configure date display options
// formatDistanceWithinHours: Shows relative time (e.g., "2 hours ago") for dates within the specified hours
// Default: 48 hours. Set to 0 to disable relative time display
{
"formatDistanceWithinHours": 48
}`,
DateTime: `// Configure datetime display options
// formatDistanceWithinHours: Shows relative time (e.g., "2 hours ago") for dates within the specified hours
// Default: 48 hours. Set to 0 to disable relative time display
{
"formatDistanceWithinHours": 48
}`,
Default: `// No settings required`,
File:
`// provide type of file: 'hex', 'base64' or 'file'
// example:
{
"type": "hex"
}
`,
Code:
`// provide language of code to highlight: 'html', 'css', 'typescript', 'yaml', 'markdown'
// example:
Foreign_key: `// Provide settings for foreign key widget
{
"language": "html"
"column_name": "", // copy the name of the column you selected
"referenced_column_name": "",
"referenced_table_name": ""
}
`,
Image:
`// provide image height in px to dispaly in table
// prefix: optional URL prefix to prepend to image source
// example:
{
"height": 100
"height": 100,
"prefix": "https://example.com/images/"
}
}
`,
URL: `// No settings required`,
Phone:
`// Configure international phone number widget
JSON: `// No settings required`,
Money: `// Configure money widget settings
// example:
{
"preferred_countries": ["US", "GB", "CA"],
"enable_placeholder": true,
"phone_validation": true
"default_currency": "USD",
"show_currency_selector": false,
"decimal_places": 2,
"allow_negative": true
}
`,
Country: `// Configure country display options
// Example:
Number: `// Configure number display with unit conversion
// Example units: "bytes", "meters", "seconds", "grams"
{
"show_flag": true,
"allow_null": false
"unit": null
}`,
Password:
`// provide algorithm to encrypt your password, one of:
//sha1, sha3, sha224, sha256, sha512, sha384, bcrypt, scrypt, argon2, pbkdf2.
// example:

{
"encrypt": true,
"algorithm": "sha256"
}

`,
Foreign_key: `// Provide settings for foreign key widget
Phone:
`// Configure international phone number widget
// example:
{
"column_name": "", // copy the name of the column you selected
"referenced_column_name": "",
"referenced_table_name": ""
"preferred_countries": ["US", "GB", "CA"],
"enable_placeholder": true,
"phone_validation": true
}
`,
Money: `// Configure money widget settings
// example:
Range: `// Configure the minimum, maximum and step values for the range
// Default: min = 0, max = 100, step = 1
{
"default_currency": "USD",
"show_currency_selector": false,
"decimal_places": 2,
"allow_negative": true
"min": 0,
"max": 100,
"step": 1
}
`,
Color: `// Optional: Specify output format for color values
// Supported formats: "hex", "hex_hash" (default), "rgb", "hsl"
// Example configuration:
Readonly: `// No settings required`,
Select:
`// provide array of options to map database value (key 'value') in human readable value (key 'label');
// for example:
// AK => Alaska,
// CA => California

{
"format": "hex_hash" // Will display colors as "#FF5733"
"allow_null": true,
"options": [
{
"value": "UA",
"label": "🇺🇦 Ukraine"
},
{
"value": "PL",
"label": "🇵🇱 Poland"
},
{
"value": "US",
"label": "🇺🇸 United States"
}
]
}`,
String: `// No settings required`,
Textarea: `// provide number of strings to show.
{
"rows": 5
}`,
Time: `// No settings required`,
URL: `// prefix: optional URL prefix to prepend to the href
// example:
{
"prefix": "https://example.com/"
}

// Format options:
// - "hex": Display as "FF5733" (no hash)
// - "hex_hash": Display as "#FF5733" (default)
// - "rgb": Display as "rgb(255, 87, 51)"
// - "hsl": Display as "hsl(9, 100%, 60%)"`,
UUID: `// Configure UUID generation version and parameters
`,
UUID: `// Configure UUID generation version and parameters
// Available versions: "v1", "v3", "v4" (default), "v5", "v7"
// For v3/v5: provide namespace and optionally name
{
"version": "v4",
"namespace": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"name": ""
}
`
`,
}

constructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@
width: 100%;
}

.country-input-container {
display: flex;
align-items: center;
width: 100%;
}

.country-flag-prefix {
font-size: 20px;
margin-right: 8px;
line-height: 1;
}

.country-input {
flex: 1;
border: none !important;
outline: none !important;
background: transparent !important;
}

.country-flag {
margin-right: 8px;
font-size: 16px;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
<mat-form-field class="country-form-field" appearance="outline">
<mat-label>{{normalizedLabel}}</mat-label>
<input type="text" matInput
[required]="required" [disabled]="disabled" [readonly]="readonly"
attr.data-testid="record-{{label}}-country"
[formControl]="countryControl"
[matAutocomplete]="auto">
<div class="country-input-container">
<span *ngIf="selectedCountryFlag && showFlag" class="country-flag-prefix">{{selectedCountryFlag}}</span>
<input type="text" matInput
[required]="required" [disabled]="disabled" [readonly]="readonly"
attr.data-testid="record-{{label}}-country"
[formControl]="countryControl"
[matAutocomplete]="auto"
class="country-input">
</div>
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayFn">
<mat-option *ngFor="let country of filteredCountries | async"
[value]="country"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class CountryEditComponent extends BaseEditFieldComponent {
public countryControl = new FormControl<{value: string | null, label: string, flag: string} | string>('');
public filteredCountries: Observable<{value: string | null, label: string, flag: string}[]>;
public showFlag: boolean = true;
public selectedCountryFlag: string = '';

originalOrder = () => { return 0; }

Expand Down Expand Up @@ -58,7 +59,16 @@ export class CountryEditComponent extends BaseEditFieldComponent {
private setupAutocomplete(): void {
this.filteredCountries = this.countryControl.valueChanges.pipe(
startWith(''),
map(value => this._filter(typeof value === 'string' ? value : (value?.label || '')))
map(value => {
// Update flag when value changes
if (typeof value === 'object' && value !== null) {
this.selectedCountryFlag = value.flag;
} else if (typeof value === 'string') {
// Clear flag if user is typing
this.selectedCountryFlag = '';
}
return this._filter(typeof value === 'string' ? value : (value?.label || ''));
})
);
}

Expand All @@ -67,6 +77,7 @@ export class CountryEditComponent extends BaseEditFieldComponent {
const country = this.countries.find(c => c.value === this.value);
if (country) {
this.countryControl.setValue(country);
this.selectedCountryFlag = country.flag;
}
}
}
Expand All @@ -81,19 +92,22 @@ export class CountryEditComponent extends BaseEditFieldComponent {

onCountrySelected(selectedCountry: {value: string | null, label: string, flag: string}): void {
this.value = selectedCountry.value;
this.selectedCountryFlag = selectedCountry.flag;
this.onFieldChange.emit(this.value);
}

displayFn(country: any): string {
return country ? country.label : '';
if (!country) return '';
// Only return the country label, flag is shown separately
return typeof country === 'string' ? country : country.label;
}

private loadCountries(): void {
this.countries = COUNTRIES.map(country => ({
value: country.code,
label: country.name,
flag: getCountryFlag(country.code)
}));
})).toSorted((a, b) => a.label.localeCompare(b.label));

if (this.widgetStructure?.widget_params?.allow_null || this.structure?.allow_null) {
this.countries = [{ value: null, label: '', flag: '' }, ...this.countries];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<div class="image-box">
<mat-form-field class="image-box__url" appearance="outline">
<mat-label>{{normalizedLabel}}</mat-label>
<span *ngIf="prefix" matTextPrefix>{{prefix}}</span>
<input matInput type="text" name="{{label}}-{{key}}" #image="ngModel"
[required]="required" [disabled]="disabled" [readonly]="readonly"
urlValidator
[urlPrefix]="prefix"
attr.data-testid="record-{{label}}-image"
[(ngModel)]="value" (ngModelChange)="onFieldChange.emit($event)">
<mat-error *ngIf="image.errors?.isInvalidURL">URL is invalid.</mat-error>
</mat-form-field>
<img *ngIf="!image.errors?.isInvalidURL" [src]="value" class="image-box__image">
<img *ngIf="!image.errors?.isInvalidURL" [src]="imageUrl" class="image-box__image">
</div>
Loading