import * as plugins from '../../../plugins.js';
import * as interfaces from '../../../../ts_interfaces/index.js';
import * as appstate from '../../../appstate.js';
import { viewHostCss } from '../../shared/index.js';
import {
DeesElement,
customElement,
html,
state,
css,
cssManager,
type TemplateResult,
} from '@design.estate/dees-element';
@customElement('gitops-view-actions')
export class GitopsViewActions extends DeesElement {
@state()
accessor lastScanTimestamp: number = 0;
@state()
accessor isScanning: boolean = false;
@state()
accessor lastResult: {
connectionsScanned: number;
secretsFound: number;
errors: string[];
durationMs: number;
} | null = null;
@state()
accessor statusError: string = '';
public static styles = [
cssManager.defaultStyles,
viewHostCss,
css`
.action-cards {
display: grid;
grid-template-columns: 1fr;
gap: 24px;
max-width: 720px;
}
.action-card {
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
padding: 28px;
}
.action-card-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 8px;
}
.action-card-description {
font-size: 13px;
color: #999;
line-height: 1.5;
margin-bottom: 20px;
}
.info-grid {
display: grid;
grid-template-columns: 140px 1fr;
gap: 8px 16px;
margin-bottom: 20px;
font-size: 13px;
}
.info-label {
color: #888;
}
.info-value {
color: #ddd;
font-family: monospace;
}
.info-value.scanning {
color: #f0c040;
}
.info-value.error {
color: #ff6060;
}
.button-row {
display: flex;
gap: 12px;
align-items: center;
}
.errors-list {
margin-top: 12px;
max-height: 160px;
overflow-y: auto;
font-size: 12px;
color: #ff8080;
font-family: monospace;
line-height: 1.6;
background: rgba(255, 0, 0, 0.05);
border-radius: 6px;
padding: 8px 12px;
}
`,
];
public render(): TemplateResult {
const lastScanFormatted = this.lastScanTimestamp
? new Date(this.lastScanTimestamp).toLocaleString()
: 'Never';
return html`
Actions
System actions and maintenance tasks
Secrets Cache Scan
Secrets are automatically scanned and cached every 24 hours.
Use "Force Full Scan" to trigger an immediate refresh of all secrets
across all connections, projects, and groups.
Status
${this.isScanning ? 'Scanning...' : 'Idle'}
Last Scan
${lastScanFormatted}
${this.lastResult ? html`
Connections
${this.lastResult.connectionsScanned}
Secrets Found
${this.lastResult.secretsFound}
Duration
${(this.lastResult.durationMs / 1000).toFixed(1)}s
${this.lastResult.errors.length > 0 ? html`
Errors
${this.lastResult.errors.length}
` : ''}
` : ''}
${this.statusError ? html`
${this.statusError}
` : ''}
${this.lastResult?.errors?.length ? html`
${this.lastResult.errors.map((e) => html`
${e}
`)}
` : ''}
this.forceScan()}
>Force Full Scan
this.refreshStatus()}
>Refresh Status
`;
}
async firstUpdated() {
await this.refreshStatus();
}
private getIdentity(): interfaces.data.IIdentity | null {
return appstate.loginStatePart.getState().identity;
}
private async refreshStatus(): Promise {
const identity = this.getIdentity();
if (!identity) return;
try {
this.statusError = '';
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetScanStatus
>('/typedrequest', 'getScanStatus');
const response = await typedRequest.fire({ identity });
this.lastScanTimestamp = response.lastScanTimestamp;
this.isScanning = response.isScanning;
this.lastResult = response.lastResult;
} catch (err) {
this.statusError = `Failed to get status: ${err}`;
}
}
private async forceScan(): Promise {
const identity = this.getIdentity();
if (!identity) return;
try {
this.statusError = '';
this.isScanning = true;
const typedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_ForceScanSecrets
>('/typedrequest', 'forceScanSecrets');
const response = await typedRequest.fire({ identity });
this.lastResult = {
connectionsScanned: response.connectionsScanned,
secretsFound: response.secretsFound,
errors: response.errors,
durationMs: response.durationMs,
};
this.lastScanTimestamp = Date.now();
this.isScanning = false;
} catch (err) {
this.statusError = `Scan failed: ${err}`;
this.isScanning = false;
}
}
}