fix(ops-view-vpn): show target profile names in VPN forms and load profile candidates for autocomplete
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@serve.zone/dcrouter',
|
||||
version: '13.0.7',
|
||||
version: '13.0.8',
|
||||
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ export class OpsViewVpn extends DeesElement {
|
||||
async connectedCallback() {
|
||||
await super.connectedCallback();
|
||||
await appstate.vpnStatePart.dispatchAction(appstate.fetchVpnAction, null);
|
||||
// Ensure target profiles are loaded for autocomplete candidates
|
||||
await appstate.targetProfilesStatePart.dispatchAction(appstate.fetchTargetProfilesAction, null);
|
||||
}
|
||||
|
||||
public static styles = [
|
||||
@@ -328,7 +330,11 @@ export class OpsViewVpn extends DeesElement {
|
||||
'Routing': routingHtml,
|
||||
'VPN IP': client.assignedIp || '-',
|
||||
'Target Profiles': client.targetProfileIds?.length
|
||||
? html`${client.targetProfileIds.map(t => html`<span class="tagBadge">${t}</span>`)}`
|
||||
? html`${client.targetProfileIds.map(id => {
|
||||
const profileState = appstate.targetProfilesStatePart.getState();
|
||||
const profile = profileState?.profiles.find(p => p.id === id);
|
||||
return html`<span class="tagBadge">${profile?.name || id}</span>`;
|
||||
})}`
|
||||
: '-',
|
||||
'Description': client.description || '-',
|
||||
'Created': new Date(client.createdAt).toLocaleDateString(),
|
||||
@@ -341,13 +347,14 @@ export class OpsViewVpn extends DeesElement {
|
||||
type: ['header'],
|
||||
actionFunc: async () => {
|
||||
const { DeesModal } = await import('@design.estate/dees-catalog');
|
||||
const profileCandidates = this.getTargetProfileCandidates();
|
||||
const createModal = await DeesModal.createAndShow({
|
||||
heading: 'Create VPN Client',
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-text .key=${'clientId'} .label=${'Client ID'} .required=${true}></dees-input-text>
|
||||
<dees-input-text .key=${'description'} .label=${'Description'}></dees-input-text>
|
||||
<dees-input-text .key=${'targetProfileIds'} .label=${'Target Profile IDs (comma-separated)'}></dees-input-text>
|
||||
<dees-input-list .key=${'targetProfileNames'} .label=${'Target Profiles'} .placeholder=${'Type to search profiles...'} .candidates=${profileCandidates} .allowFreeform=${false}></dees-input-list>
|
||||
<dees-input-checkbox .key=${'forceDestinationSmartproxy'} .label=${'Force traffic through SmartProxy'} .value=${true}></dees-input-checkbox>
|
||||
<div class="hostIpGroup" style="display: none; flex-direction: column; gap: 16px;">
|
||||
<dees-input-checkbox .key=${'useHostIp'} .label=${'Get Host IP'} .value=${false}></dees-input-checkbox>
|
||||
@@ -383,9 +390,9 @@ export class OpsViewVpn extends DeesElement {
|
||||
if (!form) return;
|
||||
const data = await form.collectFormData();
|
||||
if (!data.clientId) return;
|
||||
const targetProfileIds = data.targetProfileIds
|
||||
? data.targetProfileIds.split(',').map((t: string) => t.trim()).filter(Boolean)
|
||||
: undefined;
|
||||
const targetProfileIds = this.resolveProfileNamesToIds(
|
||||
Array.isArray(data.targetProfileNames) ? data.targetProfileNames : [],
|
||||
);
|
||||
|
||||
// Apply conditional logic based on checkbox states
|
||||
const forceSmartproxy = data.forceDestinationSmartproxy ?? true;
|
||||
@@ -479,7 +486,7 @@ export class OpsViewVpn extends DeesElement {
|
||||
<div class="infoItem"><span class="infoLabel">Transport</span><span class="infoValue">${conn.transport}</span></div>
|
||||
` : ''}
|
||||
<div class="infoItem"><span class="infoLabel">Description</span><span class="infoValue">${client.description || '-'}</span></div>
|
||||
<div class="infoItem"><span class="infoLabel">Target Profiles</span><span class="infoValue">${client.targetProfileIds?.join(', ') || '-'}</span></div>
|
||||
<div class="infoItem"><span class="infoLabel">Target Profiles</span><span class="infoValue">${this.resolveProfileIdsToNames(client.targetProfileIds)?.join(', ') || '-'}</span></div>
|
||||
<div class="infoItem"><span class="infoLabel">Routing</span><span class="infoValue">${client.forceDestinationSmartproxy !== false ? 'SmartProxy' : client.useHostIp ? 'Host IP' : 'Direct'}</span></div>
|
||||
${client.useHostIp ? html`
|
||||
<div class="infoItem"><span class="infoLabel">Host IP</span><span class="infoValue">${client.useDhcp ? 'DHCP' : client.staticIp ? `Static: ${client.staticIp}` : 'Not configured'}</span></div>
|
||||
@@ -643,7 +650,8 @@ export class OpsViewVpn extends DeesElement {
|
||||
const client = actionData.item as interfaces.data.IVpnClient;
|
||||
const { DeesModal } = await import('@design.estate/dees-catalog');
|
||||
const currentDescription = client.description ?? '';
|
||||
const currentTargetProfileIds = client.targetProfileIds?.join(', ') ?? '';
|
||||
const currentTargetProfileNames = this.resolveProfileIdsToNames(client.targetProfileIds) || [];
|
||||
const profileCandidates = this.getTargetProfileCandidates();
|
||||
const currentForceSmartproxy = client.forceDestinationSmartproxy ?? true;
|
||||
const currentUseHostIp = client.useHostIp ?? false;
|
||||
const currentUseDhcp = client.useDhcp ?? false;
|
||||
@@ -659,7 +667,7 @@ export class OpsViewVpn extends DeesElement {
|
||||
content: html`
|
||||
<dees-form>
|
||||
<dees-input-text .key=${'description'} .label=${'Description'} .value=${currentDescription}></dees-input-text>
|
||||
<dees-input-text .key=${'targetProfileIds'} .label=${'Target Profile IDs (comma-separated)'} .value=${currentTargetProfileIds}></dees-input-text>
|
||||
<dees-input-list .key=${'targetProfileNames'} .label=${'Target Profiles'} .placeholder=${'Type to search profiles...'} .candidates=${profileCandidates} .allowFreeform=${false} .value=${currentTargetProfileNames}></dees-input-list>
|
||||
<dees-input-checkbox .key=${'forceDestinationSmartproxy'} .label=${'Force traffic through SmartProxy'} .value=${currentForceSmartproxy}></dees-input-checkbox>
|
||||
<div class="hostIpGroup" style="display: ${currentForceSmartproxy ? 'none' : 'flex'}; flex-direction: column; gap: 16px;">
|
||||
<dees-input-checkbox .key=${'useHostIp'} .label=${'Get Host IP'} .value=${currentUseHostIp}></dees-input-checkbox>
|
||||
@@ -690,9 +698,9 @@ export class OpsViewVpn extends DeesElement {
|
||||
const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
|
||||
if (!form) return;
|
||||
const data = await form.collectFormData();
|
||||
const targetProfileIds = data.targetProfileIds
|
||||
? data.targetProfileIds.split(',').map((t: string) => t.trim()).filter(Boolean)
|
||||
: [];
|
||||
const targetProfileIds = this.resolveProfileNamesToIds(
|
||||
Array.isArray(data.targetProfileNames) ? data.targetProfileNames : [],
|
||||
);
|
||||
|
||||
// Apply conditional logic based on checkbox states
|
||||
const forceSmartproxy = data.forceDestinationSmartproxy ?? true;
|
||||
@@ -805,4 +813,43 @@ export class OpsViewVpn extends DeesElement {
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build autocomplete candidates from loaded target profiles.
|
||||
* viewKey = profile name (displayed), payload = { id } (carried for resolution).
|
||||
*/
|
||||
private getTargetProfileCandidates() {
|
||||
const profileState = appstate.targetProfilesStatePart.getState();
|
||||
const profiles = profileState?.profiles || [];
|
||||
return profiles.map((p) => ({ viewKey: p.name, payload: { id: p.id } }));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert profile IDs to profile names (for populating edit form values).
|
||||
*/
|
||||
private resolveProfileIdsToNames(ids?: string[]): string[] | undefined {
|
||||
if (!ids?.length) return undefined;
|
||||
const profileState = appstate.targetProfilesStatePart.getState();
|
||||
const profiles = profileState?.profiles || [];
|
||||
return ids.map((id) => {
|
||||
const profile = profiles.find((p) => p.id === id);
|
||||
return profile?.name || id;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert profile names back to IDs (for saving form data).
|
||||
* Uses the dees-input-list candidates' payload when available.
|
||||
*/
|
||||
private resolveProfileNamesToIds(names: string[]): string[] | undefined {
|
||||
if (!names.length) return undefined;
|
||||
const profileState = appstate.targetProfilesStatePart.getState();
|
||||
const profiles = profileState?.profiles || [];
|
||||
return names
|
||||
.map((name) => {
|
||||
const profile = profiles.find((p) => p.name === name);
|
||||
return profile?.id;
|
||||
})
|
||||
.filter((id): id is string => !!id);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user