Initial commit

Signed-off-by: Romein van Buren <romein@vburen.nl>
This commit is contained in:
2022-06-22 17:22:19 +02:00
commit 1983f78fba
17 changed files with 3016 additions and 0 deletions

View 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)}>
&times;
</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;
}}>
&times;
</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;
}}>
&times;
</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>

View 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>