diff --git a/frontend/package.json b/frontend/package.json
index f59bf03e9..27aaf2d84 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -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"
},
diff --git a/frontend/src/app/components/dashboard/db-table-widgets/db-table-widgets.component.ts b/frontend/src/app/components/dashboard/db-table-widgets/db-table-widgets.component.ts
index d3f03ff8f..76308ef37 100644
--- a/frontend/src/app/components/dashboard/db-table-widgets/db-table-widgets.component.ts
+++ b/frontend/src/app/components/dashboard/db-table-widgets/db-table-widgets.component.ts
@@ -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
@@ -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(
diff --git a/frontend/src/app/components/ui-components/record-edit-fields/country/country.component.html b/frontend/src/app/components/ui-components/record-edit-fields/country/country.component.html
index 23a3dabdd..0ed6862ae 100644
--- a/frontend/src/app/components/ui-components/record-edit-fields/country/country.component.html
+++ b/frontend/src/app/components/ui-components/record-edit-fields/country/country.component.html
@@ -9,7 +9,7 @@
- {{country.flag}}
+ {{country.flag}}
{{country.label}}
({{country.value}})
diff --git a/frontend/src/app/components/ui-components/record-edit-fields/country/country.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/country/country.component.ts
index 1538e8f15..d95584b9d 100644
--- a/frontend/src/app/components/ui-components/record-edit-fields/country/country.component.ts
+++ b/frontend/src/app/components/ui-components/record-edit-fields/country/country.component.ts
@@ -25,6 +25,7 @@ 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; }
@@ -32,11 +33,28 @@ export class CountryEditComponent extends BaseEditFieldComponent {
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(''),
diff --git a/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.css b/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.css
new file mode 100644
index 000000000..c796719b5
--- /dev/null
+++ b/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.css
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.html b/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.html
new file mode 100644
index 000000000..6dccab057
--- /dev/null
+++ b/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.html
@@ -0,0 +1,45 @@
+
+ {{ normalizedLabel }}
+
+
+ @if (!readonly && !disabled) {
+
+ }
+
+ @if (value && validateUuid(value)) {
+
+ UUID {{ getUuidVersion(value) ? 'v' + getUuidVersion(value) : 'version unknown' }}
+
+ }
+
+ @if (value && !validateUuid(value)) {
+ Invalid UUID format
+ }
+
+
+
+@if (!readonly && !disabled && (uuidVersion === 'v3' || uuidVersion === 'v5')) {
+
+ info
+
+ UUID {{ uuidVersion }} uses namespace/name hashing. Configure these in widget parameters.
+
+
+}
\ No newline at end of file
diff --git a/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.ts
new file mode 100644
index 000000000..dfb03e859
--- /dev/null
+++ b/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.ts
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/app/components/ui-components/table-display-fields/country/country.component.html b/frontend/src/app/components/ui-components/table-display-fields/country/country.component.html
index 8acfa685c..54639a23b 100644
--- a/frontend/src/app/components/ui-components/table-display-fields/country/country.component.html
+++ b/frontend/src/app/components/ui-components/table-display-fields/country/country.component.html
@@ -1,6 +1,6 @@
-
+ {{ countryFlag }}
{{ countryName }}