feat: Add basic translation framework
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
85
public/assets/calculator.js
Normal file
@@ -0,0 +1,85 @@
|
||||
const MINIMUM_BASE_AREA = 0.5;
|
||||
const MINIMUM_AREA_THREE_RATS = 1.5;
|
||||
const AREA_PER_ADDITIONAL_RAT = 0.25;
|
||||
const MAXIMUM_FALL_HEIGHT = 0.5;
|
||||
const MINIMUM_LENGTH_LONG_SIDE = 0.8;
|
||||
const MINIMUM_LENGTH_SHORT_SIDE = 0.5;
|
||||
const MINIMUM_FLOOR_HEIGHT = 0.3;
|
||||
|
||||
const FAILED_BASE_AREA = "base_area";
|
||||
const FAILED_OVERALL_AREA = "overall_area";
|
||||
const FAILED_FALL_HEIGHT = "fall_height";
|
||||
const FAILED_NUM_RATS = "num_rats";
|
||||
const FAILED_MINIMUM_LENGTH_LONG_SIDE = "length_long_side";
|
||||
const FAILED_MINIMUM_LENGTH_SHORT_SIDE= "length_short_side";
|
||||
const FAILED_FLOOR_HEIGHT = "floor_height"
|
||||
|
||||
const FAIL_CRITERIA = {
|
||||
[FAILED_BASE_AREA]: `Die Mindestgrundfläche des Käfigs muss ${MINIMUM_BASE_AREA}m² (also z.B. 100x50cm) betragen.`,
|
||||
[FAILED_OVERALL_AREA]: "Die Gesamtfläche im Käfig ist zu klein.",
|
||||
[FAILED_FALL_HEIGHT]: `Die mögliche Fallhöhe darf nicht mehr als ${(MAXIMUM_FALL_HEIGHT * 100).toFixed(0)}cm betragen.`,
|
||||
[FAILED_FLOOR_HEIGHT]: `Der Mindestabstand zwischen Ebenen muss ${(MINIMUM_FLOOR_HEIGHT * 100).toFixed(0)}cm betragen.`,
|
||||
[FAILED_NUM_RATS]: "Es müssen mindestens 3 Ratten zusammenleben, Paarhaltung ist nicht artgerecht.",
|
||||
[FAILED_MINIMUM_LENGTH_LONG_SIDE]: `Die lange Seite des Käfig muss mindestens ${(MINIMUM_LENGTH_LONG_SIDE * 100).toFixed(0)}cm lang sein um Rennen zu ermöglichen.`,
|
||||
[FAILED_MINIMUM_LENGTH_SHORT_SIDE]: `Die kurze Seite des Käfig muss mindestens ${(MINIMUM_LENGTH_SHORT_SIDE * 100).toFixed(0)}cm lang sein.`,
|
||||
};
|
||||
|
||||
class Dimensions {
|
||||
constructor(width, depth, height) {
|
||||
this.width = width;
|
||||
this.depth = depth;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `${this.width}x${this.depth}x${this.height}`;
|
||||
}
|
||||
|
||||
static fromDict(data) {
|
||||
const { width, depth, height } = data;
|
||||
return new Dimensions(width, depth, height);
|
||||
}
|
||||
}
|
||||
|
||||
function overallAreaNeeded(numOfRats) {
|
||||
if (numOfRats < 3 || numOfRats > 15) {
|
||||
throw new Error("This formula works only from 3 to 15 rats");
|
||||
}
|
||||
return MINIMUM_AREA_THREE_RATS + (numOfRats - 3) * AREA_PER_ADDITIONAL_RAT;
|
||||
}
|
||||
|
||||
function cageCheck(dimensions, numRats, numFullFloors) {
|
||||
let failedCriteria = {};
|
||||
|
||||
if (numRats < 2 || numRats > 15) {
|
||||
failedCriteria[FAILED_NUM_RATS] = FAIL_CRITERIA[FAILED_NUM_RATS];
|
||||
}
|
||||
|
||||
const baseArea = dimensions.depth * dimensions.width;
|
||||
if (baseArea < MINIMUM_BASE_AREA) {
|
||||
failedCriteria[FAILED_BASE_AREA] = FAIL_CRITERIA[FAILED_BASE_AREA];
|
||||
}
|
||||
|
||||
const areaNeeded = overallAreaNeeded(numRats);
|
||||
if (baseArea * numFullFloors < areaNeeded) {
|
||||
failedCriteria[FAILED_OVERALL_AREA] = FAIL_CRITERIA[FAILED_OVERALL_AREA];
|
||||
}
|
||||
|
||||
if (dimensions.height / numFullFloors > MAXIMUM_FALL_HEIGHT) {
|
||||
failedCriteria[FAILED_FALL_HEIGHT] = FAIL_CRITERIA[FAILED_FALL_HEIGHT];
|
||||
}
|
||||
|
||||
if (dimensions.width < MINIMUM_LENGTH_LONG_SIDE && dimensions.depth < MINIMUM_LENGTH_LONG_SIDE) {
|
||||
failedCriteria[FAILED_MINIMUM_LENGTH_LONG_SIDE] = FAIL_CRITERIA[FAILED_MINIMUM_LENGTH_LONG_SIDE];
|
||||
}
|
||||
|
||||
if (dimensions.width < MINIMUM_LENGTH_SHORT_SIDE || dimensions.depth < MINIMUM_LENGTH_SHORT_SIDE) {
|
||||
failedCriteria[FAILED_MINIMUM_LENGTH_SHORT_SIDE] = FAIL_CRITERIA[FAILED_MINIMUM_LENGTH_SHORT_SIDE];
|
||||
}
|
||||
|
||||
if (dimensions.height / numFullFloors < MINIMUM_FLOOR_HEIGHT) {
|
||||
failedCriteria[FAILED_FLOOR_HEIGHT] = FAIL_CRITERIA[FAILED_FLOOR_HEIGHT];
|
||||
}
|
||||
|
||||
return failedCriteria;
|
||||
}
|
234
public/assets/css/style.css
Normal file
@@ -0,0 +1,234 @@
|
||||
:root {
|
||||
--primary-light-one: #5daa68;
|
||||
--primary-light-two: #4a9455;
|
||||
--primary-semidark-one: #356c3c;
|
||||
--primary-dark-one: #17311b;
|
||||
--secondary-light-one: #faf1cf;
|
||||
--secondary-light-two: #e1d7b5;
|
||||
--background-one: var(--primary-light-one);
|
||||
--background-two: var(--primary-light-two);
|
||||
--background-three: var(--secondary-light-one);
|
||||
--background-four: var(--primary-dark-one);
|
||||
--highlight-one: var(--primary-dark-one);
|
||||
--highlight-one-text: var(--secondary-light-one);
|
||||
--highlight-two: var(--primary-semidark-one);
|
||||
--text-one: var(--secondary-light-one);
|
||||
--shadow-one: var(--primary-dark-one);
|
||||
--text-two: var(--primary-dark-one);
|
||||
--text-three: var(--primary-light-one);
|
||||
--shadow-three: var(--primary-dark-one);
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--background-one);
|
||||
color: var(--text-two);
|
||||
}
|
||||
.content {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
word-wrap: break-word;
|
||||
color: var(--text-one);
|
||||
text-shadow: 2px 2px var(--shadow-one);
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.container-form {
|
||||
background: var(--background-three);
|
||||
color: var(--text-two);
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.input-element {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.output-element {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.slidecontainer {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.slider {
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
background: #d3d3d3;
|
||||
outline: none;
|
||||
opacity: 0.7;
|
||||
-webkit-transition: .2s;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
.slider:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 23px;
|
||||
height: 24px;
|
||||
border: 0;
|
||||
background: url('../img/logo_transparent.png');
|
||||
background-size: 100% 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
width: 23px;
|
||||
height: 24px;
|
||||
border: 0;
|
||||
background: url('../img/logo_transparent.png');
|
||||
background-size: 100% 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-group button {
|
||||
padding: 10px;
|
||||
border: none;
|
||||
background-color: var(--primary-dark-one);
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.input-group input {
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border: 1px solid #ccc;
|
||||
background-color: var(--secondary-light-one);
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.cards {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
|
||||
.card {
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
|
||||
transition: 0.3s;
|
||||
margin: 0.4em;
|
||||
min-width: 300px;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
flex: 0 1 calc(25% - 0.5em);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.card-active {
|
||||
border: 3px solid var(--highlight-two);
|
||||
}
|
||||
|
||||
.info-container {
|
||||
padding: 2px 16px;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
|
||||
.measurement {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.cage-selector {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.form-measurements {
|
||||
max-height: 90%;
|
||||
}
|
||||
|
||||
.form-measurements input {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
input.measurement {
|
||||
margin-top: 5%;
|
||||
}
|
||||
|
||||
.container-inputs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.navigation-sticky {
|
||||
background-color: var(--secondary-light-one);
|
||||
color: var(--primary-light-one);
|
||||
padding: 16px;
|
||||
margin: 0;
|
||||
border-bottom-right-radius: 8px;
|
||||
border: none;
|
||||
font-weight: bold;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 200px;
|
||||
background-color: var(--primary-dark-one);
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
}
|
BIN
public/assets/favicon/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
public/assets/favicon/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
public/assets/favicon/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
public/assets/favicon/favicon-16x16.png
Normal file
After Width: | Height: | Size: 432 B |
BIN
public/assets/favicon/favicon-32x32.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
public/assets/favicon/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
1
public/assets/favicon/site.webmanifest
Normal file
@@ -0,0 +1 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
BIN
public/assets/img/checked.png
Normal file
After Width: | Height: | Size: 11 KiB |
1
public/assets/img/info.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg class="text-grey-dark" width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor"><path d="M9.00026 12.6C9.00026 12.6 9.00026 12.1224 9.00026 11.5333V8.86666C9.00026 8.57211 8.76148 8.33333 8.46693 8.33333H7.93359" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M8.73346 5.26666C8.58619 5.26666 8.4668 5.38605 8.4668 5.53333C8.4668 5.68061 8.58619 5.8 8.73346 5.8C8.88074 5.8 9.00013 5.68061 9.00013 5.53333C9.00013 5.38605 8.88074 5.26666 8.73346 5.26666V5.26666" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M9 17C13.4183 17 17 13.4183 17 9C17 4.58172 13.4183 1 9 1C4.58172 1 1 4.58172 1 9C1 13.4183 4.58172 17 9 17Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>
|
After Width: | Height: | Size: 834 B |
BIN
public/assets/img/logo.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
public/assets/img/logo_transparent.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
public/assets/img/logo_transparent_small.png
Normal file
After Width: | Height: | Size: 546 B |
BIN
public/assets/img/savic-95-double.jpg
Normal file
After Width: | Height: | Size: 3.2 MiB |
BIN
public/assets/img/savic-xl.jpeg
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
public/assets/img/tiaki.jpeg
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
public/assets/img/unchecked.png
Normal file
After Width: | Height: | Size: 639 B |