Skip to content

Commit 0e73e58

Browse files
committed
feat(new tool): SLA Computer
Fix CorentinTh#738
1 parent 80e46c9 commit 0e73e58

File tree

9 files changed

+488
-10
lines changed

9 files changed

+488
-10
lines changed

components.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ declare module '@vue/runtime-core' {
166166
NProgress: typeof import('naive-ui')['NProgress']
167167
NScrollbar: typeof import('naive-ui')['NScrollbar']
168168
NSlider: typeof import('naive-ui')['NSlider']
169+
NSpace: typeof import('naive-ui')['NSpace']
169170
NStatistic: typeof import('naive-ui')['NStatistic']
170171
NSwitch: typeof import('naive-ui')['NSwitch']
171172
NTable: typeof import('naive-ui')['NTable']
@@ -186,6 +187,7 @@ declare module '@vue/runtime-core' {
186187
RouterLink: typeof import('vue-router')['RouterLink']
187188
RouterView: typeof import('vue-router')['RouterView']
188189
RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
190+
SlaCalculator: typeof import('./src/tools/sla-calculator/sla-calculator.vue')['default']
189191
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
190192
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
191193
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,14 @@
4141
"@tiptap/pm": "2.1.6",
4242
"@tiptap/starter-kit": "2.1.6",
4343
"@tiptap/vue-3": "2.0.3",
44+
"@types/big.js": "^6.2.2",
4445
"@vicons/material": "^0.12.0",
4546
"@vicons/tabler": "^0.12.0",
4647
"@vueuse/core": "^10.3.0",
4748
"@vueuse/head": "^1.0.0",
4849
"@vueuse/router": "^10.0.0",
4950
"bcryptjs": "^2.4.3",
51+
"big.js": "^6.2.2",
5052
"change-case": "^4.1.2",
5153
"colord": "^2.9.3",
5254
"composerize-ts": "^0.6.2",
@@ -74,9 +76,11 @@
7476
"netmask": "^2.0.2",
7577
"node-forge": "^1.3.1",
7678
"oui-data": "^1.0.10",
79+
"parse-duration": "^1.1.0",
7780
"pdf-signature-reader": "^1.4.2",
7881
"pinia": "^2.0.34",
7982
"plausible-tracker": "^0.3.8",
83+
"pretty-ms": "^9.1.0",
8084
"qrcode": "^1.5.1",
8185
"sql-formatter": "^13.0.0",
8286
"ua-parser-js": "^1.0.35",

pnpm-lock.yaml

Lines changed: 45 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/composable/queryParams.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { useRouteQuery } from '@vueuse/router';
22
import { computed } from 'vue';
3+
import { useStorage } from '@vueuse/core';
34

4-
export { useQueryParam };
5+
export { useQueryParam, useQueryParamOrStorage };
56

67
const transformers = {
78
number: {
@@ -16,6 +17,12 @@ const transformers = {
1617
fromQuery: (value: string) => value.toLowerCase() === 'true',
1718
toQuery: (value: boolean) => (value ? 'true' : 'false'),
1819
},
20+
object: {
21+
fromQuery: (value: string) => {
22+
return JSON.parse(value);
23+
},
24+
toQuery: (value: object) => JSON.stringify(value),
25+
},
1926
};
2027

2128
function useQueryParam<T>({ name, defaultValue }: { name: string; defaultValue: T }) {
@@ -33,3 +40,27 @@ function useQueryParam<T>({ name, defaultValue }: { name: string; defaultValue:
3340
},
3441
});
3542
}
43+
44+
function useQueryParamOrStorage<T>({ name, storageName, defaultValue }: { name: string; storageName: string; defaultValue: T }) {
45+
const type = typeof defaultValue;
46+
const transformer = transformers[type as keyof typeof transformers] ?? transformers.string;
47+
48+
const storageRef = useStorage(storageName, defaultValue);
49+
const proxyDefaultValue = transformer.toQuery(defaultValue as never);
50+
const proxy = useRouteQuery(name, proxyDefaultValue);
51+
52+
const r = ref(defaultValue);
53+
54+
watch(r,
55+
(value) => {
56+
proxy.value = transformer.toQuery(value as never);
57+
storageRef.value = value as never;
58+
},
59+
{ deep: true });
60+
61+
r.value = (proxy.value && proxy.value !== proxyDefaultValue
62+
? transformer.fromQuery(proxy.value) as unknown as T
63+
: storageRef.value as T) as never;
64+
65+
return r;
66+
}

src/tools/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { tool as base64FileConverter } from './base64-file-converter';
22
import { tool as base64StringConverter } from './base64-string-converter';
33
import { tool as basicAuthGenerator } from './basic-auth-generator';
4+
import { tool as slaCalculator } from './sla-calculator';
45
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
56
import { tool as numeronymGenerator } from './numeronym-generator';
67
import { tool as macAddressGenerator } from './mac-address-generator';
@@ -151,7 +152,12 @@ export const toolsByCategory: ToolCategory[] = [
151152
},
152153
{
153154
name: 'Measurement',
154-
components: [chronometer, temperatureConverter, benchmarkBuilder],
155+
components: [
156+
chronometer,
157+
temperatureConverter,
158+
benchmarkBuilder,
159+
slaCalculator,
160+
],
155161
},
156162
{
157163
name: 'Text',

src/tools/sla-calculator/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Clock } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'SLA calculator',
6+
path: '/sla-calculator',
7+
description: 'Service Level Agreement Calcultator',
8+
keywords: ['sla', 'service', 'level', 'agreement', 'calculator'],
9+
component: () => import('./sla-calculator.vue'),
10+
icon: Clock,
11+
createdAt: new Date('2024-05-11'),
12+
});
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { downTimeToSLA, slaToDowntimes } from './sla-calculator.service';
3+
4+
describe('sla-calculator', () => {
5+
describe('downTimeToSLA', () => {
6+
it('compute correct values', () => {
7+
expect(downTimeToSLA({
8+
downTimeSeconds: 100,
9+
mondayHours: 16,
10+
tuesdayHours: 6,
11+
wednesdayHours: 4,
12+
thursdayHours: 24,
13+
fridayHours: 21,
14+
saturdayHours: 16,
15+
sundayHours: 13,
16+
})).to.deep.eq({
17+
slaForDay: null,
18+
slaForMonth: 99.99361155031703,
19+
slaForQuarter: 99.99787051677234,
20+
slaForWeek: 99.97222222222223,
21+
slaForYear: 99.99946762919309,
22+
});
23+
expect(downTimeToSLA({
24+
downTimeSeconds: 86400 / 10000,
25+
})).to.deep.eq({
26+
slaForDay: 99.99,
27+
slaForMonth: 99.99967145115916,
28+
slaForQuarter: 99.99989048371972,
29+
slaForWeek: 99.99857142857142,
30+
slaForYear: 99.99997262092992,
31+
});
32+
expect(downTimeToSLA({
33+
downTimeSeconds: 86400 / 2,
34+
})).to.deep.eq({
35+
slaForDay: 50,
36+
slaForMonth: 98.35725579580689,
37+
slaForQuarter: 99.45241859860229,
38+
slaForWeek: 92.85714285714286,
39+
slaForYear: 99.86310464965058,
40+
});
41+
});
42+
});
43+
describe('slaToDowntimes', () => {
44+
it('compute correct values', () => {
45+
expect(slaToDowntimes({
46+
targetSLA: 99,
47+
mondayHours: 9,
48+
tuesdayHours: 24,
49+
wednesdayHours: 10,
50+
thursdayHours: 8,
51+
fridayHours: 3,
52+
saturdayHours: 24,
53+
sundayHours: 4,
54+
})).to.deep.eq({
55+
secondsPerDay: null,
56+
secondsPerMonth: 12835.665,
57+
secondsPerQuarter: 38506.995,
58+
secondsPerWeek: 2952,
59+
secondsPerYear: 154027.98,
60+
});
61+
expect(slaToDowntimes({
62+
targetSLA: 99.99,
63+
})).to.deep.eq({
64+
secondsPerDay: 8.64,
65+
secondsPerMonth: 262.9746,
66+
secondsPerQuarter: 788.9238,
67+
secondsPerWeek: 60.48,
68+
secondsPerYear: 3155.6952,
69+
});
70+
expect(slaToDowntimes({
71+
targetSLA: 99,
72+
})).to.deep.eq({
73+
secondsPerDay: 864,
74+
secondsPerMonth: 26297.46,
75+
secondsPerQuarter: 78892.38,
76+
secondsPerWeek: 6048,
77+
secondsPerYear: 315569.52,
78+
});
79+
});
80+
});
81+
});

0 commit comments

Comments
 (0)