fix: frontend error codes and translations

This commit is contained in:
2026-07-08 11:57:38 +02:00
parent 87bed4e1c7
commit c6bba8a40c
6 changed files with 403 additions and 357 deletions
+60 -60
View File
@@ -1,85 +1,85 @@
import { API_BASE } from "../../config/api.config";
import {API_BASE} from "../../config/api.config";
import Cookies from "js-cookie";
import type { TFunction } from "i18next";
import { fetchSettings } from "./settings";
import type { ChangePasswordIntf } from "../../misc/interfaces";
import { createApiError } from "./apiError";
import type {TFunction} from "i18next";
import {fetchSettings} from "./settings";
import type {ChangePasswordIntf} from "../../misc/interfaces";
import {createApiError} from "./apiError";
export async function isAuthenticated() {
if (Cookies.get("token")) {
const result = await fetch(`${API_BASE}/users/verify-token`, {
method: "POST",
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
if (Cookies.get("token")) {
const result = await fetch(`${API_BASE}/users/verify-token`, {
method: "POST",
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
if (result.status === 200) {
return true;
if (result.status === 200) {
return true;
}
}
}
Cookies.remove("token");
return false;
Cookies.remove("token");
return false;
}
export async function signInUser(
username: string,
password: string,
t: TFunction,
username: string,
password: string,
t: TFunction,
) {
const result = await fetch(`${API_BASE}/users/login`, {
method: "POST",
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({ username, password }),
});
const result = await fetch(`${API_BASE}/users/login`, {
method: "POST",
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({username, password}),
});
const response = await result.json();
const response = await result.json();
if (result.status === 202) {
Cookies.set("token", response.data.token);
if (result.status === 202) {
Cookies.set("token", response.data.token);
const settings = await fetchSettings();
Cookies.set("app-name", settings?.data[0].value);
Cookies.set("currency", settings?.data[1].value);
const settings = await fetchSettings();
Cookies.set("app-name", settings?.data[0].value);
Cookies.set("currency", settings?.data[1].value);
return { ok: true as const };
}
return {ok: true as const};
}
Cookies.remove("token");
throw createApiError(response.code, t(response.code || "unknown-error"));
Cookies.remove("token");
throw createApiError(response.code, t(response.code || "unknown-error"));
}
export function signOutUser() {
Cookies.remove("token");
return { ok: true as const };
Cookies.remove("token");
return {ok: true as const};
}
export const mutatePassword = async (payload: ChangePasswordIntf) => {
const result = await fetch(`${API_BASE}/users/change-password`, {
method: "POST",
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
currentPassword: payload.currentPassword,
newPassword: payload.newPassword,
}),
});
const result = await fetch(`${API_BASE}/users/change-password`, {
method: "POST",
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
currentPassword: payload.currentPassword,
newPassword: payload.newPassword,
}),
});
const response = await result.json();
const response = await result.json();
if (response.code === "su005") {
return { code: response.code };
}
if (response.code === "SU005") {
return {code: response.code};
}
throw createApiError(response.code, "Change password failed");
throw createApiError(response.code, "Change password failed");
};
+88 -88
View File
@@ -1,121 +1,121 @@
import { API_BASE } from "../../config/api.config";
import {API_BASE} from "../../config/api.config";
import Cookies from "js-cookie";
import type { ProductFormValues } from "../../misc/interfaces";
import { createApiError } from "./apiError";
import type {ProductFormValues} from "../../misc/interfaces";
import {createApiError} from "./apiError";
export const getProducts = async () => {
const result = await fetch(`${API_BASE}/products/all-products`, {
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const response = await result.json();
const result = await fetch(`${API_BASE}/products/all-products`, {
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const response = await result.json();
if (response.code === "sp002") {
return response.data;
}
if (response.code === "SP002") {
return response.data;
}
throw createApiError(response.code, "Get products failed");
throw createApiError(response.code, "Get products failed");
};
export const getProductDetails = async (uuid: string) => {
const result = await fetch(`${API_BASE}/products/view?uuid=${uuid}`, {
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const result = await fetch(`${API_BASE}/products/view?uuid=${uuid}`, {
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const response = await result.json();
const response = await result.json();
if (response.code === "sp003") {
return response.data;
}
if (response.code === "SP003") {
return response.data;
}
throw createApiError(response.code, "Get product details failed");
throw createApiError(response.code, "Get product details failed");
};
export const mutateProduct = async (
values: ProductFormValues,
itemUUID: string,
values: ProductFormValues,
itemUUID: string,
) => {
const payload = {
...values,
expiry_date: values.expiry_date || null,
bottling_date: values.bottling_date || null,
};
const payload = {
...values,
expiry_date: values.expiry_date || null,
bottling_date: values.bottling_date || null,
};
const result = await fetch(
`${API_BASE}/products/mutate/update-item?item=${itemUUID}`,
{
method: "POST",
body: JSON.stringify(payload),
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
},
);
const result = await fetch(
`${API_BASE}/products/mutate/update-item?item=${itemUUID}`,
{
method: "POST",
body: JSON.stringify(payload),
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
},
);
const response = await result.json();
const response = await result.json();
if (response.code === "sp005") {
return response.data;
}
if (response.code === "SP005") {
return response.data;
}
throw createApiError(response.code, "Update product failed");
throw createApiError(response.code, "Update product failed");
};
export const deleteSelectedProducts = async (uuids: string[]) => {
const result = await fetch(`${API_BASE}/products/delete-selection`, {
method: "POST",
body: JSON.stringify(uuids),
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const result = await fetch(`${API_BASE}/products/delete-selection`, {
method: "POST",
body: JSON.stringify(uuids),
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const response = await result.json();
const response = await result.json();
if (response.code === "sp006") {
return { success: true };
}
if (response.code === "SP006") {
return {success: true};
}
throw createApiError(response.code, "Delete products failed");
throw createApiError(response.code, "Delete products failed");
};
export const createProduct = async (values: ProductFormValues) => {
const payload = {
name: values.name,
description: values.description,
price: values.price,
amount: values.amount,
storage_location: values.storage_location_uuid,
expiry_date: values.expiry_date || null,
bottling_date: values.bottling_date || null,
};
const payload = {
name: values.name,
description: values.description,
price: values.price,
amount: values.amount,
storage_location: values.storage_location_uuid,
expiry_date: values.expiry_date || null,
bottling_date: values.bottling_date || null,
};
const result = await fetch(`${API_BASE}/products/new-product`, {
method: "POST",
body: JSON.stringify(payload),
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const result = await fetch(`${API_BASE}/products/new-product`, {
method: "POST",
body: JSON.stringify(payload),
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const response = await result.json();
const response = await result.json();
if (response.code === "sp001") {
return response.data;
}
if (response.code === "SP001") {
return response.data;
}
throw createApiError(response.code, "Create product failed");
throw createApiError(response.code, "Create product failed");
};
+30 -30
View File
@@ -1,43 +1,43 @@
import { API_BASE } from "../../config/api.config";
import {API_BASE} from "../../config/api.config";
import Cookies from "js-cookie";
import type { SettingsIntf } from "../../misc/interfaces";
import { createApiError } from "./apiError";
import type {SettingsIntf} from "../../misc/interfaces";
import {createApiError} from "./apiError";
export const fetchSettings = async () => {
const result = await fetch(`${API_BASE}/users/settings`, {
method: "GET",
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const result = await fetch(`${API_BASE}/users/settings`, {
method: "GET",
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const response = await result.json();
const response = await result.json();
if (response.code === "su004") {
return { success: true, data: response.data, code: response.code };
}
if (response.code === "SU004") {
return {success: true, data: response.data, code: response.code};
}
throw createApiError(response.code, "Fetch settings failed");
throw createApiError(response.code, "Fetch settings failed");
};
export const mutateSettings = async (payload: SettingsIntf) => {
const result = await fetch(`${API_BASE}/users/update-app-settings`, {
method: "POST",
body: JSON.stringify(payload),
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const result = await fetch(`${API_BASE}/users/update-app-settings`, {
method: "POST",
body: JSON.stringify(payload),
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const response = await result.json();
const response = await result.json();
if (response.code === "su003") {
return { success: true, code: response.code };
}
if (response.code === "SU003") {
return {success: true, code: response.code};
}
throw createApiError(response.code, "Update settings failed");
throw createApiError(response.code, "Update settings failed");
};
+61 -61
View File
@@ -1,87 +1,87 @@
import { API_BASE } from "../../config/api.config";
import {API_BASE} from "../../config/api.config";
import Cookies from "js-cookie";
import type { NewStorage, Storage } from "../../misc/interfaces";
import { createApiError } from "./apiError";
import type {NewStorage, Storage} from "../../misc/interfaces";
import {createApiError} from "./apiError";
export const getStorages = async () => {
const result = await fetch(`${API_BASE}/storage/all-storages`, {
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const result = await fetch(`${API_BASE}/storage/all-storages`, {
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const response = await result.json();
const response = await result.json();
if (response.code === "ss001") {
return response.data;
}
if (response.code === "SS001") {
return response.data;
}
throw createApiError(response.code, "Get storages failed");
throw createApiError(response.code, "Get storages failed");
};
export const mutateNewStorage = async (values: NewStorage) => {
const result = await fetch(`${API_BASE}/storage/new-storage`, {
method: "POST",
body: JSON.stringify(values),
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const result = await fetch(`${API_BASE}/storage/new-storage`, {
method: "POST",
body: JSON.stringify(values),
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const response = await result.json();
const response = await result.json();
if (response.code === "ss002") {
return response.data;
}
if (response.code === "SS002") {
return response.data;
}
throw createApiError(response.code, "Create storage failed");
throw createApiError(response.code, "Create storage failed");
};
export const updateStorage = async (
uuid: string,
values: Pick<Storage, "name" | "description">,
uuid: string,
values: Pick<Storage, "name" | "description">,
) => {
const result = await fetch(
`${API_BASE}/storage/update-storage?storageUUID=${uuid}`,
{
method: "POST",
body: JSON.stringify(values),
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
},
);
const result = await fetch(
`${API_BASE}/storage/update-storage?storageUUID=${uuid}`,
{
method: "POST",
body: JSON.stringify(values),
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
},
);
const response = await result.json();
const response = await result.json();
if (response.code === "ss003") {
return response.data;
}
if (response.code === "ss003") {
return response.data;
}
throw createApiError(response.code, "Update storage failed");
throw createApiError(response.code, "Update storage failed");
};
export const deleteStorage = async (uuid: string) => {
const result = await fetch(`${API_BASE}/storage/delete?uuid=${uuid}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const result = await fetch(`${API_BASE}/storage/delete?uuid=${uuid}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${Cookies.get("token") || ""}`,
"Content-Type": "application/json",
Accept: "application/json",
},
});
const response = await result.json();
const response = await result.json();
if (response.code === "ss004") {
return { success: true, code: response.code };
}
if (response.code === "SS004") {
return {success: true, code: response.code};
}
throw createApiError(response.code, "Delete storage failed");
throw createApiError(response.code, "Delete storage failed");
};
+82 -59
View File
@@ -1,61 +1,84 @@
{
"app-title": "Stockhome",
"add": "Hinzufügen",
"storages": "Lager",
"rows-per-page": "Zeilen pro Seite",
"details": "Details",
"username": "Benutzername",
"password": "Passwort",
"eu001": "Falscher Benutzername oder Passwort!",
"login": "Login",
"product-name": "Produktname",
"price": "Preis",
"stock": "Bestand",
"storage-place": "Lagerplatz",
"expiry-date": "Ablaufdatum",
"bottling-date": "Abfüllungsdatum",
"inventory": "Inventar",
"actions": "Aktionen",
"description": "Beschreibung",
"product-details": "Produktdetails",
"save": "Speichern",
"profile": "Profil",
"settings": "Einstellungen",
"storage-delete-info": "WARNUNG: Wenn Sie einen Lagerort entfernen, werden alle darin gespeicherten Artikel/Produkte ebenfalls entfernt. Das kann nicht rückgängig gemacht werden!",
"storage-name": "Lagername",
"created-at": "Erstellt am",
"updated-at": "Aktualisiert am",
"delete": "Löschen",
"amount": "Menge",
"inventory-subtitle": "Hier können Sie alle Ihre gelagerten Artikel einsehen.",
"add-product": "Produkt hinzufügen",
"add-product-subtitle": "Hier können Sie Informationen zu einem neuen Produkt eingeben.",
"app-name": "App-Name",
"app-name-sub": "Wird in der Seitenleiste und im Login-Bildschirm angezeigt.",
"currency": "Währung",
"currency-sub": "Wird für die Preisangaben in allen Inventaransichten verwendet.",
"quick-tips": "Schnelle Tipps",
"quick-tips-1": "Aktualisieren Sie den App-Namen, um die Seitenleiste und den Login-Header zu personalisieren.",
"quick-tips-2": "Wählen Sie einen Währungscode, der zur Preisanzeige passt, z. B. EUR, CHF oder USD.",
"settings-sub": "Verwalten Sie Ihre App-Einstellungen und Standardwerte.",
"preferences": "Einstellungen",
"selected": "ausgewählt",
"logout": "Abmelden",
"pcs": "Stk.",
"change-translation": "English",
"new-storage-title": "Neuer Lagerort",
"new-storage-content": "Geben Sie hier die Daten für einen neuen Lagerort ein.",
"menu": "Menü",
"close": "Schließen",
"product-details-quick": "Schnelle Produktdetails",
"download-qr-code": "QR-Code herunterladen",
"change-password": "Passwort ändern",
"new-password-title": "Hier ändern Sie Ihr Passwort.",
"current-password": "Aktuelles Passwort",
"new-password": "Neues Passwort",
"new-password-rep": "Neues Passwort wiederholen",
"change": "Ändern",
"error": "Fehler",
"success": "Erfolg",
"submit": "Absenden"
"app-title": "Stockhome",
"add": "Hinzufügen",
"storages": "Lager",
"rows-per-page": "Zeilen pro Seite",
"details": "Details",
"username": "Benutzername",
"password": "Passwort",
"login": "Login",
"product-name": "Produktname",
"price": "Preis",
"stock": "Bestand",
"storage-place": "Lagerplatz",
"expiry-date": "Ablaufdatum",
"bottling-date": "Abfüllungsdatum",
"inventory": "Inventar",
"actions": "Aktionen",
"description": "Beschreibung",
"product-details": "Produktdetails",
"save": "Speichern",
"profile": "Profil",
"settings": "Einstellungen",
"storage-delete-info": "WARNUNG: Wenn Sie einen Lagerort entfernen, werden alle darin gespeicherten Artikel/Produkte ebenfalls entfernt. Das kann nicht rückgängig gemacht werden!",
"storage-name": "Lagername",
"created-at": "Erstellt am",
"updated-at": "Aktualisiert am",
"delete": "Löschen",
"amount": "Menge",
"inventory-subtitle": "Hier können Sie alle Ihre gelagerten Artikel einsehen.",
"add-product": "Produkt hinzufügen",
"add-product-subtitle": "Hier können Sie Informationen zu einem neuen Produkt eingeben.",
"app-name": "App-Name",
"app-name-sub": "Wird in der Seitenleiste und im Login-Bildschirm angezeigt.",
"currency": "Währung",
"currency-sub": "Wird für die Preisangaben in allen Inventaransichten verwendet.",
"quick-tips": "Schnelle Tipps",
"quick-tips-1": "Aktualisieren Sie den App-Namen, um die Seitenleiste und den Login-Header zu personalisieren.",
"quick-tips-2": "Wählen Sie einen Währungscode, der zur Preisanzeige passt, z. B. EUR, CHF oder USD.",
"settings-sub": "Verwalten Sie Ihre App-Einstellungen und Standardwerte.",
"preferences": "Einstellungen",
"selected": "ausgewählt",
"logout": "Abmelden",
"pcs": "Stk.",
"change-translation": "English",
"new-storage-title": "Neuer Lagerort",
"new-storage-content": "Geben Sie hier die Daten für einen neuen Lagerort ein.",
"menu": "Menü",
"close": "Schließen",
"product-details-quick": "Schnelle Produktdetails",
"download-qr-code": "QR-Code herunterladen",
"change-password": "Passwort ändern",
"new-password-title": "Hier ändern Sie Ihr Passwort.",
"current-password": "Aktuelles Passwort",
"new-password": "Neues Passwort",
"new-password-rep": "Neues Passwort wiederholen",
"change": "Ändern",
"error": "Fehler",
"success": "Erfolg",
"submit": "Absenden",
"EG001": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.",
"EU001": "Falscher Benutzername oder Passwort!",
"EU002": "Ihr Konto wurde deaktiviert. Bitte wenden Sie sich an einen Administrator.",
"EU003": "Bitte geben Sie Benutzername und Passwort ein.",
"EU004": "Einige erforderliche Angaben fehlen. Bitte überprüfen Sie Ihre Eingabe.",
"EU005": "Anmeldung aufgrund eines technischen Problems fehlgeschlagen. Bitte versuchen Sie es erneut.",
"EU006": "Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut.",
"EU007": "Passwort konnte nicht geändert werden. Bitte versuchen Sie es erneut.",
"EU008": "Ihre Einstellungen konnten nicht gespeichert werden. Bitte versuchen Sie es erneut.",
"EU009": "Ihre Einstellungen konnten nicht geladen werden. Bitte versuchen Sie es erneut.",
"ES001": "Bitte füllen Sie alle erforderlichen Felder für den Lagerort aus.",
"ES002": "Einige erforderliche Lagerinformationen fehlen. Bitte überprüfen Sie Ihre Eingabe.",
"ES003": "Der Lagerort konnte nicht aktualisiert werden. Bitte versuchen Sie es erneut.",
"ES004": "Der Lagerort konnte nicht gelöscht werden. Bitte versuchen Sie es erneut.",
"ES005": "Keine Lagerorte gefunden.",
"ES006": "Der Lagerort konnte nicht erstellt werden. Bitte versuchen Sie es erneut.",
"EP001": "Das Produkt konnte nicht erstellt werden. Bitte versuchen Sie es erneut.",
"EP002": "Keine Produkte gefunden.",
"EP003": "Produkt nicht gefunden.",
"EP004": "Die Produktmenge konnte nicht aktualisiert werden. Bitte versuchen Sie es erneut.",
"EP005": "Das Produkt konnte nicht aktualisiert werden. Bitte versuchen Sie es erneut.",
"EP006": "Das Produkt konnte nicht gelöscht werden. Bitte versuchen Sie es erneut.",
"EP007": "Bitte geben Sie mindestens einen Produktnamen ein.",
"EP008": "Einige erforderliche Produktinformationen fehlen. Bitte überprüfen Sie Ihre Eingabe."
}
+82 -59
View File
@@ -1,61 +1,84 @@
{
"app-title": "Stockhome",
"add": "Add",
"storages": "Storages",
"rows-per-page": "Rows per page",
"details": "Details",
"username": "Username",
"password": "Password",
"eu001": "Wrong username or password!",
"login": "Login",
"product-name": "Product name",
"price": "Price",
"stock": "Stock",
"storage-place": "Storage place",
"expiry-date": "Expiry Date",
"bottling-date": "Bottling Date",
"inventory": "Inventory",
"actions": "Actions",
"description": "Description",
"product-details": "Product Details",
"save": "Save",
"profile": "Profile",
"settings": "Settings",
"storage-delete-info": "WARNING: If you remove a storage all items/products that are stored in this storage will also get removed. This cannot be undone!",
"storage-name": "Storage name",
"created-at": "Created at",
"updated-at": "Updated at",
"delete": "Delete",
"amount": "Amount",
"inventory-subtitle": "Here you can inspect all of your stored items.",
"add-product": "Add product",
"add-product-subtitle": "Here you can enter information for a new product.",
"app-name": "App name",
"app-name-sub": "Displayed in the sidebar and login screen.",
"currency": "Currency",
"currency-sub": "Used for pricing across inventory views.",
"quick-tips": "Quick tips",
"quick-tips-1": "Update the app name to personalize the sidebar and login header.",
"quick-tips-2": "Choose a currency code that matches your pricing display, e.g. EUR, CHF, or USD.",
"settings-sub": "Manage your app preferences and store defaults.",
"preferences": "Preferences",
"selected": "selected",
"logout": "Logout",
"pcs": "pcs.",
"change-translation": "Deutsch",
"new-storage-title": "New Storage location",
"new-storage-content": "Enter the details for a new storage location here.",
"menu": "Menu",
"close": "Close",
"product-details-quick": "Fast product details",
"download-qr-code": "Download QR-Code",
"change-password": "Change password",
"new-password-title": "Here you can change your password.",
"current-password": "Current password",
"new-password": "New password",
"new-password-rep": "Repeat new password",
"change": "Change",
"error": "Error",
"success": "Success",
"submit": "Submit"
"app-title": "Stockhome",
"add": "Add",
"storages": "Storages",
"rows-per-page": "Rows per page",
"details": "Details",
"username": "Username",
"password": "Password",
"login": "Login",
"product-name": "Product name",
"price": "Price",
"stock": "Stock",
"storage-place": "Storage place",
"expiry-date": "Expiry Date",
"bottling-date": "Bottling Date",
"inventory": "Inventory",
"actions": "Actions",
"description": "Description",
"product-details": "Product Details",
"save": "Save",
"profile": "Profile",
"settings": "Settings",
"storage-delete-info": "WARNING: If you remove a storage all items/products that are stored in this storage will also get removed. This cannot be undone!",
"storage-name": "Storage name",
"created-at": "Created at",
"updated-at": "Updated at",
"delete": "Delete",
"amount": "Amount",
"inventory-subtitle": "Here you can inspect all of your stored items.",
"add-product": "Add product",
"add-product-subtitle": "Here you can enter information for a new product.",
"app-name": "App name",
"app-name-sub": "Displayed in the sidebar and login screen.",
"currency": "Currency",
"currency-sub": "Used for pricing across inventory views.",
"quick-tips": "Quick tips",
"quick-tips-1": "Update the app name to personalize the sidebar and login header.",
"quick-tips-2": "Choose a currency code that matches your pricing display, e.g. EUR, CHF, or USD.",
"settings-sub": "Manage your app preferences and store defaults.",
"preferences": "Preferences",
"selected": "selected",
"logout": "Logout",
"pcs": "pcs.",
"change-translation": "Deutsch",
"new-storage-title": "New Storage location",
"new-storage-content": "Enter the details for a new storage location here.",
"menu": "Menu",
"close": "Close",
"product-details-quick": "Fast product details",
"download-qr-code": "Download QR-Code",
"change-password": "Change password",
"new-password-title": "Here you can change your password.",
"current-password": "Current password",
"new-password": "New password",
"new-password-rep": "Repeat new password",
"change": "Change",
"error": "Error",
"success": "Success",
"submit": "Submit",
"EG001": "An unexpected error occurred. Please try again later.",
"EU001": "Wrong username or password!",
"EU002": "Your account has been deactivated. Please contact an administrator.",
"EU003": "Please enter both username and password.",
"EU004": "Some required information is missing. Please check your input.",
"EU005": "Login failed due to a technical issue. Please try again.",
"EU006": "Login failed. Please try again.",
"EU007": "Failed to change your password. Please try again.",
"EU008": "Your settings could not be saved. Please try again.",
"EU009": "Your settings could not be loaded. Please try again.",
"ES001": "Please fill in all required fields for the storage location.",
"ES002": "Some required storage information is missing. Please check your input.",
"ES003": "The storage location could not be updated. Please try again.",
"ES004": "The storage location could not be deleted. Please try again.",
"ES005": "No storage locations found.",
"ES006": "The storage location could not be created. Please try again.",
"EP001": "The product could not be created. Please try again.",
"EP002": "No products found.",
"EP003": "Product not found.",
"EP004": "The product amount could not be updated. Please try again.",
"EP005": "The product could not be updated. Please try again.",
"EP006": "The product could not be deleted. Please try again.",
"EP007": "Please enter at least a product name.",
"EP008": "Some required product information is missing. Please check your input."
}