fix(ops-ui): improve operations table actions and modal form handling for profiles and network targets

This commit is contained in:
2026-04-02 18:49:52 +00:00
parent 6684dc43da
commit 5202c2ea27
6 changed files with 47 additions and 30 deletions

View File

@@ -1,5 +1,13 @@
# Changelog
## 2026-04-02 - 12.2.6 - fix(ops-ui)
improve operations table actions and modal form handling for profiles and network targets
- adds section headings for the Security Profiles and Network Targets views
- updates edit and delete actions to support in-row table actions in addition to context menus
- makes create and edit dialogs query forms safely from modal content and adds early returns when forms are unavailable
- enables the database configuration in the development watch server
## 2026-04-02 - 12.2.5 - fix(dcrouter)
sync allowed tunnel edges when merged routes change

View File

@@ -49,8 +49,7 @@ const devRouter = new DcRouter({
{ clientId: 'admin-desktop', serverDefinedClientTags: ['admin'], description: 'Admin workstation' },
],
},
// Disable db/mongo for dev
dbConfig: { enabled: false },
dbConfig: { enabled: true },
});
console.log('Starting DcRouter in development mode...');

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/dcrouter',
version: '12.2.5',
version: '12.2.6',
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
}

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/dcrouter',
version: '12.2.5',
version: '12.2.6',
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
}

View File

@@ -64,6 +64,7 @@ export class OpsViewNetworkTargets extends DeesElement {
];
return html`
<ops-sectionheading>Network Targets</ops-sectionheading>
<div class="targetsContainer">
<dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
<dees-table
@@ -81,8 +82,8 @@ export class OpsViewNetworkTargets extends DeesElement {
name: 'Create Target',
iconName: 'lucide:plus',
type: ['header' as const],
actionFunc: async (_: any, table: any) => {
await this.showCreateTargetDialog(table);
actionFunc: async () => {
await this.showCreateTargetDialog();
},
},
{
@@ -96,16 +97,18 @@ export class OpsViewNetworkTargets extends DeesElement {
{
name: 'Edit',
iconName: 'lucide:pencil',
type: ['contextmenu' as const],
actionFunc: async (target: interfaces.data.INetworkTarget, table: any) => {
await this.showEditTargetDialog(target, table);
type: ['inRow', 'contextmenu'] as any,
actionFunc: async (actionData: any) => {
const target = actionData.item as interfaces.data.INetworkTarget;
await this.showEditTargetDialog(target);
},
},
{
name: 'Delete',
iconName: 'lucide:trash2',
type: ['contextmenu' as const],
actionFunc: async (target: interfaces.data.INetworkTarget) => {
type: ['inRow', 'contextmenu'] as any,
actionFunc: async (actionData: any) => {
const target = actionData.item as interfaces.data.INetworkTarget;
await this.deleteTarget(target);
},
},
@@ -115,7 +118,7 @@ export class OpsViewNetworkTargets extends DeesElement {
`;
}
private async showCreateTargetDialog(table: any) {
private async showCreateTargetDialog() {
const { DeesModal } = await import('@design.estate/dees-catalog');
DeesModal.createAndShow({
heading: 'Create Network Target',
@@ -128,10 +131,12 @@ export class OpsViewNetworkTargets extends DeesElement {
</dees-form>
`,
menuOptions: [
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
{
name: 'Create',
action: async (modalArg: any) => {
const form = modalArg.shadowRoot!.querySelector('dees-form');
const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
if (!form) return;
const data = await form.collectFormData();
await appstate.profilesTargetsStatePart.dispatchAction(appstate.createTargetAction, {
@@ -143,12 +148,11 @@ export class OpsViewNetworkTargets extends DeesElement {
modalArg.destroy();
},
},
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
],
});
}
private async showEditTargetDialog(target: interfaces.data.INetworkTarget, table: any) {
private async showEditTargetDialog(target: interfaces.data.INetworkTarget) {
const hostStr = Array.isArray(target.host) ? target.host.join(', ') : target.host;
const { DeesModal } = await import('@design.estate/dees-catalog');
@@ -163,10 +167,12 @@ export class OpsViewNetworkTargets extends DeesElement {
</dees-form>
`,
menuOptions: [
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
{
name: 'Save',
action: async (modalArg: any) => {
const form = modalArg.shadowRoot!.querySelector('dees-form');
const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
if (!form) return;
const data = await form.collectFormData();
await appstate.profilesTargetsStatePart.dispatchAction(appstate.updateTargetAction, {
@@ -179,7 +185,6 @@ export class OpsViewNetworkTargets extends DeesElement {
modalArg.destroy();
},
},
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
],
});
}

View File

@@ -64,6 +64,7 @@ export class OpsViewSecurityProfiles extends DeesElement {
];
return html`
<ops-sectionheading>Security Profiles</ops-sectionheading>
<div class="profilesContainer">
<dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
<dees-table
@@ -89,8 +90,8 @@ export class OpsViewSecurityProfiles extends DeesElement {
name: 'Create Profile',
iconName: 'lucide:plus',
type: ['header' as const],
actionFunc: async (_: any, table: any) => {
await this.showCreateProfileDialog(table);
actionFunc: async () => {
await this.showCreateProfileDialog();
},
},
{
@@ -104,16 +105,18 @@ export class OpsViewSecurityProfiles extends DeesElement {
{
name: 'Edit',
iconName: 'lucide:pencil',
type: ['contextmenu' as const],
actionFunc: async (profile: interfaces.data.ISecurityProfile, table: any) => {
await this.showEditProfileDialog(profile, table);
type: ['inRow', 'contextmenu'] as any,
actionFunc: async (actionData: any) => {
const profile = actionData.item as interfaces.data.ISecurityProfile;
await this.showEditProfileDialog(profile);
},
},
{
name: 'Delete',
iconName: 'lucide:trash2',
type: ['contextmenu' as const],
actionFunc: async (profile: interfaces.data.ISecurityProfile) => {
type: ['inRow', 'contextmenu'] as any,
actionFunc: async (actionData: any) => {
const profile = actionData.item as interfaces.data.ISecurityProfile;
await this.deleteProfile(profile);
},
},
@@ -123,7 +126,7 @@ export class OpsViewSecurityProfiles extends DeesElement {
`;
}
private async showCreateProfileDialog(table: any) {
private async showCreateProfileDialog() {
const { DeesModal } = await import('@design.estate/dees-catalog');
DeesModal.createAndShow({
heading: 'Create Security Profile',
@@ -137,10 +140,12 @@ export class OpsViewSecurityProfiles extends DeesElement {
</dees-form>
`,
menuOptions: [
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
{
name: 'Create',
action: async (modalArg: any) => {
const form = modalArg.shadowRoot!.querySelector('dees-form');
const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
if (!form) return;
const data = await form.collectFormData();
const ipAllowList: string[] = Array.isArray(data.ipAllowList) ? data.ipAllowList : [];
const ipBlockList: string[] = Array.isArray(data.ipBlockList) ? data.ipBlockList : [];
@@ -158,12 +163,11 @@ export class OpsViewSecurityProfiles extends DeesElement {
modalArg.destroy();
},
},
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
],
});
}
private async showEditProfileDialog(profile: interfaces.data.ISecurityProfile, table: any) {
private async showEditProfileDialog(profile: interfaces.data.ISecurityProfile) {
const { DeesModal } = await import('@design.estate/dees-catalog');
DeesModal.createAndShow({
heading: `Edit Profile: ${profile.name}`,
@@ -177,10 +181,12 @@ export class OpsViewSecurityProfiles extends DeesElement {
</dees-form>
`,
menuOptions: [
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
{
name: 'Save',
action: async (modalArg: any) => {
const form = modalArg.shadowRoot!.querySelector('dees-form');
const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
if (!form) return;
const data = await form.collectFormData();
const ipAllowList: string[] = Array.isArray(data.ipAllowList) ? data.ipAllowList : [];
const ipBlockList: string[] = Array.isArray(data.ipBlockList) ? data.ipBlockList : [];
@@ -199,7 +205,6 @@ export class OpsViewSecurityProfiles extends DeesElement {
modalArg.destroy();
},
},
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
],
});
}