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
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"puppeteer": "^23.10.4",
"rxjs": "^7.4.0",
"tslib": "^2.8.1",
"uuid": "^11.1.0",
"validator": "^13.12.0",
"zone.js": "~0.15.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,13 @@ export class DbTableWidgetsComponent implements OnInit {
"phone_validation": true
}
`,
Country: `// No settings required`,
Country: `// Configure country display options
// Example:
{
"show_flag": true,
"allow_null": false
}
`,
Foreign_key: `// Provide settings for foreign key widget
{
"column_name": "", // copy the name of the column you selected
Expand Down Expand Up @@ -206,6 +212,15 @@ export class DbTableWidgetsComponent implements OnInit {
// - "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
// 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 @@ -9,7 +9,7 @@
<mat-option *ngFor="let country of filteredCountries | async"
[value]="country"
(onSelectionChange)="country && onCountrySelected(country)">
<span *ngIf="country.flag" class="country-flag">{{country.flag}}</span>
<span *ngIf="country.flag && showFlag" class="country-flag">{{country.flag}}</span>
<span class="country-name">{{country.label}}</span>
<span *ngIf="country.value" class="country-code">({{country.value}})</span>
</mat-option>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,36 @@ export class CountryEditComponent extends BaseEditFieldComponent {
public countries: {value: string | null, label: string, flag: string}[] = [];
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;

originalOrder = () => { return 0; }

getCountryFlag = getCountryFlag;

ngOnInit(): void {
super.ngOnInit();
this.parseWidgetParams();
this.loadCountries();
this.setupAutocomplete();
this.setInitialValue();
}

private parseWidgetParams(): void {
if (this.widgetStructure?.widget_params) {
try {
const params = typeof this.widgetStructure.widget_params === 'string'
? JSON.parse(this.widgetStructure.widget_params)
: this.widgetStructure.widget_params;

if (params.show_flag !== undefined) {
this.showFlag = params.show_flag;
}
} catch (e) {
console.error('Error parsing country widget params:', e);
}
}
}

private setupAutocomplete(): void {
this.filteredCountries = this.countryControl.valueChanges.pipe(
startWith(''),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.uuid-field {
width: 100%;
}

.uuid-input {
font-family: 'Roboto Mono', monospace;
font-size: 14px;
}

.regenerate-button {
cursor: pointer;
}

.regenerate-button:hover {
color: var(--mat-primary);
}

.uuid-config-info {
display: flex;
align-items: center;
gap: 8px;
margin-top: -16px;
margin-bottom: 16px;
padding: 8px;
background-color: var(--mat-grey-100);
border-radius: 4px;
font-size: 12px;
}

.info-icon {
font-size: 16px;
width: 16px;
height: 16px;
color: var(--mat-grey-600);
}

.info-text {
color: var(--mat-grey-700);
}

/* Dark mode support */
@media (prefers-color-scheme: dark) {
.uuid-config-info {
background-color: var(--mat-grey-800);
}

.info-icon {
color: var(--mat-grey-400);
}

.info-text {
color: var(--mat-grey-300);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<mat-form-field appearance="outline" class="uuid-field">
<mat-label>{{ normalizedLabel }}</mat-label>
<input
matInput
[(ngModel)]="value"
[readonly]="readonly || disabled"
[required]="required"
(ngModelChange)="onFieldChange.emit($event)"
placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
class="uuid-input"
/>

@if (!readonly && !disabled) {
<button
mat-icon-button
matSuffix
(click)="generateUuid()"
matTooltip="Generate new UUID"
type="button"
class="regenerate-button"
>
<mat-icon>refresh</mat-icon>
</button>
}

@if (value && validateUuid(value)) {
<mat-hint align="end">
UUID {{ getUuidVersion(value) ? 'v' + getUuidVersion(value) : 'version unknown' }}
</mat-hint>
}

@if (value && !validateUuid(value)) {
<mat-error>Invalid UUID format</mat-error>
}
</mat-form-field>

<!-- Widget configuration info for v3/v5 versions -->
@if (!readonly && !disabled && (uuidVersion === 'v3' || uuidVersion === 'v5')) {
<div class="uuid-config-info">
<mat-icon class="info-icon">info</mat-icon>
<span class="info-text">
UUID {{ uuidVersion }} uses namespace/name hashing. Configure these in widget parameters.
</span>
</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { Component, Injectable, Input, OnInit } from '@angular/core';
import { validate as uuidValidate, version as uuidVersion, v1 as uuidv1, v3 as uuidv3, v4 as uuidv4, v5 as uuidv5, v7 as uuidv7 } from 'uuid';

import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';

@Injectable()

@Component({
selector: 'app-edit-uuid',
templateUrl: './uuid.component.html',
styleUrls: ['./uuid.component.css'],
imports: [
CommonModule,
MatFormFieldModule,
MatInputModule,
MatButtonModule,
MatIconModule,
MatSelectModule,
MatTooltipModule,
FormsModule
]
})
export class UuidEditComponent extends BaseEditFieldComponent implements OnInit {
@Input() value: string;

static type = 'uuid';

public uuidVersion: string = 'v4';
public isCreateMode: boolean = true;
public namespace: string = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; // Default DNS namespace
public name: string = '';

// Available UUID versions
public availableVersions = [
{ value: 'v1', label: 'v1 (timestamp + MAC)', description: 'Timestamp and MAC address based' },
{ value: 'v3', label: 'v3 (MD5 hash)', description: 'MD5 hash of namespace and name' },
{ value: 'v4', label: 'v4 (random)', description: 'Random UUID (most common)' },
{ value: 'v5', label: 'v5 (SHA-1 hash)', description: 'SHA-1 hash of namespace and name' },
{ value: 'v7', label: 'v7 (timestamp)', description: 'Timestamp-based sortable UUID' }
];

// Standard namespaces for v3/v5
public namespaces = [
{ value: '6ba7b810-9dad-11d1-80b4-00c04fd430c8', label: 'DNS' },
{ value: '6ba7b811-9dad-11d1-80b4-00c04fd430c8', label: 'URL' },
{ value: '6ba7b812-9dad-11d1-80b4-00c04fd430c8', label: 'OID' },
{ value: '6ba7b814-9dad-11d1-80b4-00c04fd430c8', label: 'X500' }
];

ngOnInit(): void {
super.ngOnInit();

// Determine if we're in create or update mode
this.isCreateMode = !this.value || this.value === '';

// Parse widget parameters
if (this.widgetStructure?.widget_params) {
try {
const params = typeof this.widgetStructure.widget_params === 'string'
? JSON.parse(this.widgetStructure.widget_params)
: this.widgetStructure.widget_params;

if (params.version) {
this.uuidVersion = params.version;
}
if (params.namespace) {
this.namespace = params.namespace;
}
if (params.name) {
this.name = params.name;
}
} catch (e) {
console.error('Error parsing UUID widget params:', e);
}
}

// Generate UUID on create
if (this.isCreateMode) {
this.generateUuid();
}
}

generateUuid(): void {
let uuid = '';

try {
switch (this.uuidVersion) {
case 'v1':
uuid = uuidv1();
break;
case 'v3':
if (this.name) {
uuid = uuidv3(this.name, this.namespace);
} else {
uuid = uuidv3(Date.now().toString(), this.namespace);
}
break;
case 'v4':
uuid = uuidv4();
break;
case 'v5':
if (this.name) {
uuid = uuidv5(this.name, this.namespace);
} else {
uuid = uuidv5(Date.now().toString(), this.namespace);
}
break;
case 'v7':
uuid = uuidv7();
break;
default:
uuid = uuidv4();
}

this.value = uuid;
this.onFieldChange.emit(this.value);
} catch (error) {
console.error('Error generating UUID:', error);
// Fallback to v4
this.value = uuidv4();
this.onFieldChange.emit(this.value);
}
}

validateUuid(value: string): boolean {
return uuidValidate(value);
}

getUuidVersion(value: string): number | false {
try {
return uuidVersion(value);
} catch {
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="field-display">
<span class="field-value">
<!--<span class="country-flag">{{ countryFlag }}</span>-->
<span *ngIf="showFlag && countryFlag" class="country-flag">{{ countryFlag }}</span>
<span>{{ countryName }}</span>
</span>
<button type="button" mat-icon-button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ export class CountryDisplayComponent extends BaseTableDisplayFieldComponent impl

public countryName: string = '';
public countryFlag: string = '';
public showFlag: boolean = true;

ngOnInit(): void {
this.parseWidgetParams();

if (this.value) {
const country = COUNTRIES.find(c => c.code === this.value);
this.countryName = country ? country.name : this.value;
Expand All @@ -30,4 +33,20 @@ export class CountryDisplayComponent extends BaseTableDisplayFieldComponent impl
this.countryFlag = '';
}
}

private parseWidgetParams(): void {
if (this.widgetStructure?.widget_params) {
try {
const params = typeof this.widgetStructure.widget_params === 'string'
? JSON.parse(this.widgetStructure.widget_params)
: this.widgetStructure.widget_params;

if (params.show_flag !== undefined) {
this.showFlag = params.show_flag;
}
} catch (e) {
console.error('Error parsing country widget params:', e);
}
}
}
}
Loading