mirror of
https://github.com/smartyellow/status.git
synced 2025-07-19 12:54:04 +00:00
268
gui/components/formautotestfield.svelte
Normal file
268
gui/components/formautotestfield.svelte
Normal file
@ -0,0 +1,268 @@
|
||||
<script>
|
||||
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import Toggle from 'components/webdesq/toggle.svelte';
|
||||
import { operatorNames } from '../../lib/operators';
|
||||
import { realValueNames } from '../../lib/realvalues';
|
||||
|
||||
export let value = [];
|
||||
export let specs = {};
|
||||
export let readonly = true;
|
||||
export let language = 'en';
|
||||
export let translate = s => s;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const defaultEndpoint = {
|
||||
uri: '',
|
||||
headers: [],
|
||||
requirements: [],
|
||||
};
|
||||
const defaultReq = {
|
||||
type: 'httpstatus',
|
||||
truth: 'true',
|
||||
operator: 'equal',
|
||||
string: ''
|
||||
};
|
||||
const defaultHeader = {
|
||||
name: '',
|
||||
value: '',
|
||||
};
|
||||
|
||||
const changeValue = () => dispatch('changeValue', value) && console.log(value);;
|
||||
const appendEndpoint = () => value = [...value, defaultEndpoint];
|
||||
|
||||
function removeEndpoint(i) {
|
||||
value.splice(i, 1);
|
||||
value = value;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#if specs.label}
|
||||
<span class="label" class:alignright={specs.labelPosition === 'right'}>
|
||||
{translate(specs.label, language)}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
{#each value as endpoint, iEndpoint (endpoint)}
|
||||
<div class="endpoint">
|
||||
<div>
|
||||
<label for="uri-{iEndpoint}">
|
||||
{translate('Endpoint URI', language)}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<input
|
||||
id="uri-{iEndpoint}"
|
||||
type="text"
|
||||
placeholder="https://"
|
||||
disabled={readonly}
|
||||
bind:value={endpoint.uri}
|
||||
on:focus
|
||||
on:blur={changeValue}
|
||||
/>
|
||||
<button on:click={() => removeEndpoint(iEndpoint)}>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if endpoint.headers?.length > 0}
|
||||
<strong>{translate('Headers', language)}</strong>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{translate('Header name', language)}</th>
|
||||
<th>{translate('Value', language)}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{#each endpoint.headers as header, iHeader (header)}
|
||||
<tr>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
placeholder={translate('name...', language)}
|
||||
disabled={readonly}
|
||||
bind:value={header.name}
|
||||
on:focus
|
||||
on:blur={changeValue}
|
||||
/>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="flex">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={translate('value...', language)}
|
||||
disabled={readonly}
|
||||
bind:value={header.value}
|
||||
on:focus
|
||||
on:blur={changeValue}
|
||||
/>
|
||||
<button on:click={() => {
|
||||
endpoint.headers.splice(iHeader, 1);
|
||||
endpoint = endpoint;
|
||||
}}>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<button on:click={() => endpoint.headers = [...endpoint.headers, defaultHeader]}>
|
||||
{translate('add request header', language)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if endpoint.requirements?.length > 0}
|
||||
<strong>{translate('Requirements', language)}</strong>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{translate('Real value', language)}</th>
|
||||
<th class="center">{translate('Truth', language)}</th>
|
||||
<th>{translate('Operator', language)}</th>
|
||||
<th>{translate('Value', language)}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{#each endpoint.requirements as req, iReq (req)}
|
||||
<tr>
|
||||
<td>
|
||||
<select
|
||||
bind:value={req.type}
|
||||
disabled={readonly}
|
||||
on:focus
|
||||
on:keyup={changeValue}
|
||||
on:change={changeValue}
|
||||
on:blur={changeValue}
|
||||
>
|
||||
{#each Object.keys(realValueNames) as valName}
|
||||
<option value={valName}>{realValueNames[valName]}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td class="center">
|
||||
<Toggle
|
||||
inline
|
||||
labels={{
|
||||
on: translate('is', language),
|
||||
off: translate('isn\'t', language)
|
||||
}}
|
||||
bind:value={req.truth}
|
||||
on:change={changeValue}
|
||||
{readonly}
|
||||
{language}
|
||||
{translate}
|
||||
/>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<select
|
||||
bind:value={req.operator}
|
||||
disabled={readonly}
|
||||
on:focus
|
||||
on:keyup={changeValue}
|
||||
on:change={changeValue}
|
||||
on:blur={changeValue}
|
||||
>
|
||||
{#each Object.keys(operatorNames) as opName}
|
||||
<option value={opName}>{operatorNames[opName]}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="flex">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={translate('value...', language)}
|
||||
disabled={readonly}
|
||||
bind:value={req.string}
|
||||
on:focus
|
||||
on:blur={changeValue}
|
||||
/>
|
||||
<button on:click={() => {
|
||||
endpoint.requirements.splice(iReq, 1);
|
||||
endpoint = endpoint;
|
||||
}}>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<button on:click={() => endpoint.requirements = [...endpoint.requirements, defaultReq]}>
|
||||
{translate('add requirement', language)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<div>
|
||||
<button on:click={appendEndpoint}>
|
||||
{translate('add endpoint', language)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div:not(:last-child) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
label, strong {
|
||||
font-weight: 700;
|
||||
line-height: 1em;
|
||||
margin-bottom: .7em;
|
||||
display: block;
|
||||
}
|
||||
label:not(:first-child), strong:not(:first-child) {
|
||||
margin-top: 1em;
|
||||
padding-top: 1em;
|
||||
border-top: 1px solid rgba(0, 0, 0, .2);
|
||||
}
|
||||
input, select {
|
||||
padding: 0.4em;
|
||||
}
|
||||
.endpoint {
|
||||
border: 1px solid rgba(226, 226, 226, .76);
|
||||
background-color: rgba(70, 90, 131, .07);
|
||||
border-radius: 3px;
|
||||
padding: .5rem;
|
||||
}
|
||||
table {
|
||||
margin-left: -2px;
|
||||
width: calc(100% + 2px);
|
||||
}
|
||||
thead tr th {
|
||||
text-align: left;
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
gap: 3px;
|
||||
}
|
||||
.flex input {
|
||||
flex: 1 0;
|
||||
}
|
||||
.flex button {
|
||||
min-width: 35px;
|
||||
flex: 0 1;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
152
gui/components/formoutagetablefield.svelte
Normal file
152
gui/components/formoutagetablefield.svelte
Normal file
@ -0,0 +1,152 @@
|
||||
<script>
|
||||
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { api } from 'helpers/webdesq/stores.js';
|
||||
|
||||
export let value = ''; // contains webservice id
|
||||
export let specs = {};
|
||||
export let language = 'en';
|
||||
export let translate = s => s;
|
||||
|
||||
const icons = {
|
||||
server: '<path d="M0 308.58v150.83a57.73 57.73 0 0 0 10.69 33.38H757.3a57.62 57.62 0 0 0 10.7-33.37V308.58a57.73 57.73 0 0 0-10.69-33.38H10.7A57.76 57.76 0 0 0 0 308.58Zm665.6 81.82a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 .01-25.6ZM640 352a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.61Zm-25.6 38.4a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 0-25.6ZM588.8 352a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.61Zm-25.6 38.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6ZM537.6 352a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 .01-25.6ZM512 390.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6ZM486.4 352a12.8 12.8 0 1 1-.01 25.61 12.8 12.8 0 0 1 0-25.61Zm-25.6 38.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6ZM435.2 352a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.61Zm-300.8-25.6A57.67 57.67 0 0 1 192 384a57.67 57.67 0 0 1-57.6 57.6A57.67 57.67 0 0 1 76.8 384a57.67 57.67 0 0 1 57.6-57.6Zm622.91-76.8A57.76 57.76 0 0 0 768 216.22V65.38A59.05 59.05 0 0 0 709.02 6.4H58.98A59.05 59.05 0 0 0 0 65.38V216.2a57.73 57.73 0 0 0 10.69 33.39H757.3Zm-91.7-102.4a12.8 12.8 0 1 1-.02 25.6 12.8 12.8 0 0 1 .01-25.6ZM640 108.8a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.6Zm-25.6 38.4a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 0-25.6Zm-25.6-38.4a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.6Zm-25.6 38.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6Zm-25.6-38.4a12.8 12.8 0 1 1-.01 25.6 12.8 12.8 0 0 1 .01-25.6ZM512 147.2a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6Zm-25.6-38.4a12.8 12.8 0 1 1-.01 25.61 12.8 12.8 0 0 1 0-25.6Zm-25.6 38.4a12.8 12.8 0 1 1 0 25.6 12.8 12.8 0 0 1 0-25.6Zm-25.6-38.4a12.8 12.8 0 1 1 0 25.61 12.8 12.8 0 0 1 0-25.6ZM134.4 83.2a57.67 57.67 0 0 1 57.6 57.6 57.67 57.67 0 0 1-57.6 57.6 57.67 57.67 0 0 1-57.6-57.6 57.67 57.67 0 0 1 57.6-57.6ZM10.69 518.4A57.76 57.76 0 0 0 0 551.78v150.83c0 32.53 26.46 59 58.98 59H709a59.05 59.05 0 0 0 58.99-59V551.79a57.73 57.73 0 0 0-10.69-33.38Zm123.7 166.4a57.67 57.67 0 0 1-57.59-57.6 57.67 57.67 0 0 1 57.6-57.6 57.67 57.67 0 0 1 57.6 57.6 57.67 57.67 0 0 1-57.6 57.6Zm300.8-64a12.8 12.8 0 1 1 .02-25.6 12.8 12.8 0 0 1-.01 25.6Zm25.61 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1-.01 25.6Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1 0 25.6Zm25.6 38.4a12.8 12.8 0 1 1 0-25.61 12.8 12.8 0 0 1 0 25.6Zm25.6-38.4a12.8 12.8 0 1 1 0-25.6 12.8 12.8 0 0 1 0 25.6Zm25.6 38.4a12.8 12.8 0 1 1 .01-25.6 12.8 12.8 0 0 1-.01 25.6Zm25.6-38.4a12.8 12.8 0 1 1 0-25.6 12.8 12.8 0 0 1 0 25.6Zm25.6 38.4a12.8 12.8 0 1 1 .01-25.61 12.8 12.8 0 0 1 0 25.6Zm0 0"/>',
|
||||
};
|
||||
const severity = {
|
||||
major: {
|
||||
name: 'major',
|
||||
class: 'l1',
|
||||
},
|
||||
minor: {
|
||||
name: 'minor',
|
||||
class: 'l2',
|
||||
},
|
||||
scheduled: {
|
||||
name: 'scheduled',
|
||||
class: 'l3',
|
||||
},
|
||||
none: {
|
||||
name: 'no impact',
|
||||
class: 'l4',
|
||||
},
|
||||
}
|
||||
const dispatch = createEventDispatcher();
|
||||
const service = value; // for clarity
|
||||
let outages = [];
|
||||
|
||||
function openOutage(title, id) {
|
||||
dispatch('openitem', {
|
||||
type: 'smartyellow/webserviceoutages',
|
||||
title: title,
|
||||
icon: icons.server,
|
||||
closeable: true,
|
||||
isNew: false,
|
||||
data: { id: id },
|
||||
});
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
outages = (await api.get('/outages')).filter(o => o.services?.includes(service));
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
{#if specs.label}
|
||||
<span class="label" class:alignright={specs.labelPosition === 'right'}>
|
||||
{translate(specs.label, language)}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
<div class="table-container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{translate('id', language)}</th>
|
||||
<th>{translate('name', language)}</th>
|
||||
<th>{translate('severity', language)}</th>
|
||||
<th class="center"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{#each outages as outage}
|
||||
{@const name = outage.name[language] || outage.name.en}
|
||||
<tr>
|
||||
<td>{outage.id}</td>
|
||||
<td>{name}</td>
|
||||
<td>
|
||||
{#if severity[outage.severity]}
|
||||
<span class="state {severity[outage.severity]?.class}">
|
||||
{severity[outage.severity]?.name}
|
||||
</span>
|
||||
{:else}
|
||||
<span class="state l0">
|
||||
{translate('unclassified', language)}
|
||||
</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="center">
|
||||
<button class="small" on:click={() => openOutage(name, outage.id)}>
|
||||
{translate('open', language)}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.table-container {
|
||||
overflow: auto;
|
||||
}
|
||||
.table {
|
||||
table-layout: fixed;
|
||||
min-width: 100%;
|
||||
border-spacing: 0;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
th {
|
||||
padding: 3px 6px;
|
||||
border: 1px solid #ccc;
|
||||
border-width: 0 1px 1px 0;
|
||||
background: #eee;
|
||||
font-weight: normal;
|
||||
user-select: none;
|
||||
box-sizing: border-box;
|
||||
text-align: left;
|
||||
}
|
||||
th:first-child,
|
||||
td:first-child {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
td {
|
||||
position: relative;
|
||||
border: 1px solid #ccc;
|
||||
border-width: 0 1px 1px 0;
|
||||
padding: 3px 6px;
|
||||
background: #fff;
|
||||
text-align: left;
|
||||
}
|
||||
tr > td:first-child {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
span.state.l0 {
|
||||
background-color: #808080;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
button.small {
|
||||
padding: 3px 10px;
|
||||
min-width: 20px;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user