221 lines
7.5 KiB
TypeScript
221 lines
7.5 KiB
TypeScript
import {
|
|
DeesElement,
|
|
html,
|
|
customElement,
|
|
type TemplateResult,
|
|
css,
|
|
state,
|
|
cssManager,
|
|
} from '@design.estate/dees-element';
|
|
import * as appstate from '../appstate.js';
|
|
import * as interfaces from '../../dist_ts_interfaces/index.js';
|
|
import { viewHostCss } from './shared/css.js';
|
|
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
'ops-view-networktargets': OpsViewNetworkTargets;
|
|
}
|
|
}
|
|
|
|
@customElement('ops-view-networktargets')
|
|
export class OpsViewNetworkTargets extends DeesElement {
|
|
@state()
|
|
accessor profilesState: appstate.IProfilesTargetsState = appstate.profilesTargetsStatePart.getState()!;
|
|
|
|
constructor() {
|
|
super();
|
|
const sub = appstate.profilesTargetsStatePart.select().subscribe((newState) => {
|
|
this.profilesState = newState;
|
|
});
|
|
this.rxSubscriptions.push(sub);
|
|
}
|
|
|
|
async connectedCallback() {
|
|
await super.connectedCallback();
|
|
await appstate.profilesTargetsStatePart.dispatchAction(appstate.fetchProfilesAndTargetsAction, null);
|
|
}
|
|
|
|
public static styles = [
|
|
cssManager.defaultStyles,
|
|
viewHostCss,
|
|
css`
|
|
.targetsContainer {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 24px;
|
|
}
|
|
`,
|
|
];
|
|
|
|
public render(): TemplateResult {
|
|
const targets = this.profilesState.targets;
|
|
|
|
const statsTiles: IStatsTile[] = [
|
|
{
|
|
id: 'totalTargets',
|
|
title: 'Total Targets',
|
|
type: 'number',
|
|
value: targets.length,
|
|
icon: 'lucide:server',
|
|
description: 'Reusable network targets',
|
|
color: '#8b5cf6',
|
|
},
|
|
];
|
|
|
|
return html`
|
|
<ops-sectionheading>Network Targets</ops-sectionheading>
|
|
<div class="targetsContainer">
|
|
<dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
|
|
<dees-table
|
|
.heading1=${'Network Targets'}
|
|
.heading2=${'Reusable host:port destinations for routes'}
|
|
.data=${targets}
|
|
.displayFunction=${(target: interfaces.data.INetworkTarget) => ({
|
|
Name: target.name,
|
|
Host: Array.isArray(target.host) ? target.host.join(', ') : target.host,
|
|
Port: target.port,
|
|
Description: target.description || '-',
|
|
})}
|
|
.dataActions=${[
|
|
{
|
|
name: 'Create Target',
|
|
iconName: 'lucide:plus',
|
|
type: ['header' as const],
|
|
actionFunc: async () => {
|
|
await this.showCreateTargetDialog();
|
|
},
|
|
},
|
|
{
|
|
name: 'Refresh',
|
|
iconName: 'lucide:rotateCw',
|
|
type: ['header' as const],
|
|
actionFunc: async () => {
|
|
await appstate.profilesTargetsStatePart.dispatchAction(appstate.fetchProfilesAndTargetsAction, null);
|
|
},
|
|
},
|
|
{
|
|
name: 'Edit',
|
|
iconName: 'lucide:pencil',
|
|
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: ['inRow', 'contextmenu'] as any,
|
|
actionFunc: async (actionData: any) => {
|
|
const target = actionData.item as interfaces.data.INetworkTarget;
|
|
await this.deleteTarget(target);
|
|
},
|
|
},
|
|
]}
|
|
></dees-table>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
private async showCreateTargetDialog() {
|
|
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
DeesModal.createAndShow({
|
|
heading: 'Create Network Target',
|
|
content: html`
|
|
<dees-form>
|
|
<dees-input-text .key=${'name'} .label=${'Name'} .required=${true}></dees-input-text>
|
|
<dees-input-text .key=${'host'} .label=${'Host'} .required=${true}></dees-input-text>
|
|
<dees-input-text .key=${'port'} .label=${'Port'} .required=${true} .value=${'443'}></dees-input-text>
|
|
<dees-input-text .key=${'description'} .label=${'Description'}></dees-input-text>
|
|
</dees-form>
|
|
`,
|
|
menuOptions: [
|
|
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
|
|
{
|
|
name: 'Create',
|
|
action: async (modalArg: any) => {
|
|
const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
|
|
if (!form) return;
|
|
const data = await form.collectFormData();
|
|
|
|
await appstate.profilesTargetsStatePart.dispatchAction(appstate.createTargetAction, {
|
|
name: String(data.name),
|
|
description: data.description ? String(data.description) : undefined,
|
|
host: String(data.host),
|
|
port: parseInt(String(data.port)) || 443,
|
|
});
|
|
modalArg.destroy();
|
|
},
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
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');
|
|
DeesModal.createAndShow({
|
|
heading: `Edit Target: ${target.name}`,
|
|
content: html`
|
|
<dees-form>
|
|
<dees-input-text .key=${'name'} .label=${'Name'} .value=${target.name}></dees-input-text>
|
|
<dees-input-text .key=${'host'} .label=${'Host'} .value=${hostStr}></dees-input-text>
|
|
<dees-input-text .key=${'port'} .label=${'Port'} .value=${String(target.port)}></dees-input-text>
|
|
<dees-input-text .key=${'description'} .label=${'Description'} .value=${target.description || ''}></dees-input-text>
|
|
</dees-form>
|
|
`,
|
|
menuOptions: [
|
|
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
|
|
{
|
|
name: 'Save',
|
|
action: async (modalArg: any) => {
|
|
const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
|
|
if (!form) return;
|
|
const data = await form.collectFormData();
|
|
|
|
await appstate.profilesTargetsStatePart.dispatchAction(appstate.updateTargetAction, {
|
|
id: target.id,
|
|
name: String(data.name),
|
|
description: data.description ? String(data.description) : undefined,
|
|
host: String(data.host),
|
|
port: parseInt(String(data.port)) || 443,
|
|
});
|
|
modalArg.destroy();
|
|
},
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
private async deleteTarget(target: interfaces.data.INetworkTarget) {
|
|
await appstate.profilesTargetsStatePart.dispatchAction(appstate.deleteTargetAction, {
|
|
id: target.id,
|
|
force: false,
|
|
});
|
|
|
|
const currentState = appstate.profilesTargetsStatePart.getState()!;
|
|
if (currentState.error?.includes('in use')) {
|
|
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
DeesModal.createAndShow({
|
|
heading: 'Target In Use',
|
|
content: html`<p>${currentState.error} Force delete?</p>`,
|
|
menuOptions: [
|
|
{
|
|
name: 'Force Delete',
|
|
action: async (modalArg: any) => {
|
|
await appstate.profilesTargetsStatePart.dispatchAction(appstate.deleteTargetAction, {
|
|
id: target.id,
|
|
force: true,
|
|
});
|
|
modalArg.destroy();
|
|
},
|
|
},
|
|
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
|
|
],
|
|
});
|
|
}
|
|
}
|
|
}
|