diff --git a/parfum-shop/public/nasser-marmor-hover.webm b/parfum-shop/public/nasser-marmor-hover.webm
index ae6e876..da09beb 100644
Binary files a/parfum-shop/public/nasser-marmor-hover.webm and b/parfum-shop/public/nasser-marmor-hover.webm differ
diff --git a/parfum-shop/server/catalog.js b/parfum-shop/server/catalog.js
index f2f95b7..60333e9 100644
--- a/parfum-shop/server/catalog.js
+++ b/parfum-shop/server/catalog.js
@@ -11,7 +11,7 @@ export const catalogProducts = [
slug: "discovery-set",
name: "Discovery Set",
kind: "discovery_set",
- size_label: "6 x 2ml",
+ size_label: "6 x 2 ml",
price_cents: 4800,
discovery_credit_cents: 4800,
},
@@ -19,18 +19,18 @@ export const catalogProducts = [
{
id: `${perfume.slug}-sample`,
slug: perfume.slug,
- name: `${perfume.name} Sample`,
+ name: `${perfume.name} Probe`,
kind: "sample",
- size_label: "2ml",
+ size_label: "2 ml Probe",
price_cents: parsePriceCents(perfume.prices.sample),
discovery_credit_cents: 0,
},
{
id: `${perfume.slug}-full`,
slug: perfume.slug,
- name: `${perfume.name} Full Size`,
+ name: `${perfume.name} 50 ml Flakon`,
kind: "full_size",
- size_label: "50ml",
+ size_label: "50 ml Flakon",
price_cents: parsePriceCents(perfume.prices.full),
discovery_credit_cents: 0,
},
diff --git a/parfum-shop/server/index.js b/parfum-shop/server/index.js
index d9d2a05..f76465f 100644
--- a/parfum-shop/server/index.js
+++ b/parfum-shop/server/index.js
@@ -25,7 +25,7 @@ const readBody = async (req) =>
req.on("data", (chunk) => {
raw += chunk;
if (raw.length > 1_000_000) {
- reject(new Error("Request body too large"));
+ reject(new Error("Anfrage ist zu gross."));
req.destroy();
}
});
@@ -37,7 +37,7 @@ const readBody = async (req) =>
try {
resolve(JSON.parse(raw));
} catch {
- reject(new Error("Invalid JSON"));
+ reject(new Error("Ungültiges JSON."));
}
});
req.on("error", reject);
@@ -130,7 +130,7 @@ const authenticate = (req) => {
const requireAuth = (req, res) => {
const auth = authenticate(req);
if (!auth) {
- json(res, 401, { error: "Please log in to continue." });
+ json(res, 401, { error: "Bitte melde dich an, um fortzufahren." });
return null;
}
return auth;
@@ -175,7 +175,7 @@ const getAvailableDiscounts = (userId, rows) => {
type: "discovery",
creditId: discovery.id,
amount_cents: Math.min(discovery.amount_cents, fullTotal),
- label: "Discovery Set credit",
+ label: "Discovery-Set-Gutschrift",
});
}
@@ -194,7 +194,7 @@ const getAvailableDiscounts = (userId, rows) => {
slug: row.slug,
product_id: row.product_id,
amount_cents: Math.min(sample.amount_cents, row.price_cents * row.quantity),
- label: `${row.name.replace(" Full Size", "")} sample credit`,
+ label: `Proben-Gutschrift für ${row.name.replace(" 50 ml Flakon", "")}`,
});
}
}
@@ -355,13 +355,13 @@ const register = async (req, res) => {
const surname = String(body.surname || "").trim();
if (!firstName || !surname || !email || !password) {
- json(res, 400, { error: "First name, surname, email, and password are required." });
+ json(res, 400, { error: "Vorname, Nachname, E-Mail und Passwort sind erforderlich." });
return;
}
const existing = db.prepare("SELECT id FROM users WHERE email = ?").get(email);
if (existing) {
- json(res, 409, { error: "An account with this email already exists." });
+ json(res, 409, { error: "Mit dieser E-Mail existiert bereits ein Konto." });
return;
}
@@ -389,7 +389,7 @@ const login = async (req, res) => {
const user = db.prepare("SELECT * FROM users WHERE email = ?").get(email);
if (!user || !verifyPassword(password, user.password_salt, user.password_hash)) {
- json(res, 401, { error: "Invalid email or password." });
+ json(res, 401, { error: "E-Mail oder Passwort ist ungültig." });
return;
}
@@ -415,7 +415,7 @@ const patchProfile = async (req, res, user) => {
});
if (!firstName || !surname) {
- json(res, 400, { error: "First name and surname are required." });
+ json(res, 400, { error: "Vorname und Nachname sind erforderlich." });
return;
}
@@ -459,7 +459,7 @@ const addCartItem = async (req, res, user) => {
const quantity = Math.max(1, Number(body.quantity || 1));
const product = db.prepare("SELECT * FROM products WHERE id = ?").get(productId);
if (!product) {
- json(res, 404, { error: "Product not found." });
+ json(res, 404, { error: "Produkt nicht gefunden." });
return;
}
const timestamp = now();
@@ -472,7 +472,7 @@ const addCartItem = async (req, res, user) => {
).run(user.id, productId, quantity, timestamp, timestamp);
json(res, 200, {
cart: getCart(user.id),
- message: `${quantity} x ${product.name} added.`,
+ message: `${quantity} x ${product.name} wurde in den Warenkorb gelegt.`,
});
};
@@ -480,7 +480,7 @@ const patchCartItem = async (req, res, user, productId) => {
const body = await readBody(req);
const quantity = Number(body.quantity);
if (!Number.isFinite(quantity)) {
- json(res, 400, { error: "Quantity is required." });
+ json(res, 400, { error: "Die Menge ist erforderlich." });
return;
}
if (quantity <= 0) {
@@ -502,7 +502,7 @@ const checkout = async (req, res, user) => {
const body = await readBody(req);
const rows = getCartRows(user.id);
if (rows.length === 0) {
- json(res, 400, { error: "Your cart is empty." });
+ json(res, 400, { error: "Dein Warenkorb ist leer." });
return;
}
@@ -514,11 +514,11 @@ const checkout = async (req, res, user) => {
};
const paymentMethod = String(body.payment_method || body.paymentMethod || "").trim();
if (!addressFields.street_name || !addressFields.house_number || !addressFields.zip_code || !addressFields.city) {
- json(res, 400, { error: "Street name, house number, ZIP code, and city are required." });
+ json(res, 400, { error: "Strasse, Hausnummer, PLZ und Ort sind erforderlich." });
return;
}
if (!["Bill", "Card", "Twint", "PayPal"].includes(paymentMethod)) {
- json(res, 400, { error: "Choose a payment method." });
+ json(res, 400, { error: "Wähle eine Zahlungsmethode." });
return;
}
@@ -611,7 +611,7 @@ const subscribeProduct = async (req, res, user) => {
const type = String(body.type || "restock").trim();
const product = db.prepare("SELECT * FROM products WHERE id = ?").get(productId);
if (!product) {
- json(res, 404, { error: "Product not found." });
+ json(res, 404, { error: "Produkt nicht gefunden." });
return;
}
db.prepare(
@@ -620,7 +620,7 @@ const subscribeProduct = async (req, res, user) => {
).run(user.id, productId, type, now());
json(res, 200, {
ok: true,
- message: `${product.name} ${type} subscription saved.`,
+ message: `${product.name}: Benachrichtigung gespeichert.`,
subscriptions: getProductSubscriptions(user.id),
});
};
@@ -719,13 +719,13 @@ const route = async (req, res) => {
return deleteCartItem(res, user, decodeURIComponent(itemMatch[1]));
}
- json(res, 404, { error: "Route not found." });
+ json(res, 404, { error: "Route nicht gefunden." });
};
createServer((req, res) => {
route(req, res).catch((error) => {
console.error(error);
- json(res, 500, { error: "Server error." });
+ json(res, 500, { error: "Serverfehler." });
});
}).listen(PORT, () => {
console.log(`Shop API listening on http://localhost:${PORT}`);
diff --git a/parfum-shop/src/components/ProductDetailPage.jsx b/parfum-shop/src/components/ProductDetailPage.jsx
index 061bfae..be9c5b3 100644
--- a/parfum-shop/src/components/ProductDetailPage.jsx
+++ b/parfum-shop/src/components/ProductDetailPage.jsx
@@ -75,17 +75,17 @@ function ProductPurchasePanel({
subscribeToProduct,
}) {
const selectedProductId = `${perfume.slug}-${selectedSize === "sample" ? "sample" : "full"}`;
- const selectedProductLabel = selectedSize === "sample" ? "Sample" : "Full Size";
+ const selectedProductLabel = selectedSize === "sample" ? "Probe" : "50 ml Flakon";
const sizeOptions = [
{
key: "full",
- title: "Full Size 50ml",
+ title: "50 ml Flakon",
price: perfume.prices.full,
note: "Nachkauf, 500+ Anwendungen",
},
{
key: "sample",
- title: "Sample 2ml",
+ title: "Probe 2 ml",
price: perfume.prices.sample,
note: "Zum Testen, ca. 20 Anwendungen",
},
@@ -125,7 +125,7 @@ function ProductPurchasePanel({
addToCart(
selectedProductId,
1,
- `${perfume.name} ${selectedProductLabel} added.`
+ `${perfume.name} ${selectedProductLabel} wurde in den Warenkorb gelegt.`
).catch(() => {})
}
>
@@ -137,7 +137,7 @@ function ProductPurchasePanel({
type="button"
onClick={() => subscribeToProduct(selectedProductId, "restock").catch(() => {})}
>
- Restock Update abonnieren
+ Verfügbarkeits-Update abonnieren
@@ -145,7 +145,7 @@ function ProductPurchasePanel({
Discovery Set wird angerechnet
- Sample- und Set-Guthaben werden beim Full-Size-Kauf automatisch
+ Proben- und Set-Guthaben werden beim 50-ml-Kauf automatisch
abgezogen.
{discountPreviewCents > 0 && (
@@ -478,10 +478,10 @@ function ProductTestingCTA({ perfume, addToCart }) {
Lieber erst testen?
-
Sample oder Discovery Set.
+
Probe oder Discovery Set.
- Bestelle ein 2ml Sample für CHF 12 oder das komplette Discovery Set mit
- allen 6 Düften für CHF 48. Beide werden beim späteren Full-Size-Kauf
+ Bestelle eine 2-ml-Probe für CHF 12 oder das komplette Discovery Set mit
+ allen 6 Düften für CHF 48. Beide werden beim späteren 50-ml-Kauf
vollständig angerechnet.
@@ -491,17 +491,17 @@ function ProductTestingCTA({ perfume, addToCart }) {
type="button"
className="atmos-btn atmos-btn--primary"
onClick={() =>
- addToCart(`${perfume.slug}-sample`, 1, `${perfume.name} Sample added.`).catch(
+ addToCart(`${perfume.slug}-sample`, 1, `${perfume.name} Probe wurde in den Warenkorb gelegt.`).catch(
() => {}
)
}
>
- Sample bestellen — {perfume.prices.sample}
+ Probe bestellen — {perfume.prices.sample}
addToCart("discovery-set", 1, "Discovery Set added.").catch(() => {})}
+ onClick={() => addToCart("discovery-set", 1, "Discovery Set wurde in den Warenkorb gelegt.").catch(() => {})}
>
Discovery Set — CHF 48
diff --git a/parfum-shop/src/components/SharedNavbar.jsx b/parfum-shop/src/components/SharedNavbar.jsx
index ee6af67..9c56ec1 100644
--- a/parfum-shop/src/components/SharedNavbar.jsx
+++ b/parfum-shop/src/components/SharedNavbar.jsx
@@ -7,7 +7,7 @@ function SharedNavbar({ variant = "hero", active = "", brandMode = "logo" }) {
const { cart, openCart, openProfile, user } = useShop();
const { isLight, toggleTheme } = useTheme();
const cartCount = cart.total_quantity || 0;
- const cartLabel = cartCount > 0 ? `Cart ${cartCount}` : "Cart";
+ const cartLabel = cartCount > 0 ? `Warenkorb ${cartCount}` : "Warenkorb";
const cartAriaLabel =
cartCount > 0
? `Warenkorb mit ${cartCount} ${cartCount === 1 ? "Artikel" : "Artikeln"} öffnen`
@@ -58,7 +58,7 @@ function SharedNavbar({ variant = "hero", active = "", brandMode = "logo" }) {
aria-haspopup="dialog"
aria-label={user ? "Profil öffnen" : "Anmelden oder Profil öffnen"}
>
- Profile
+ Profil
discoveryStatusLabels[status] || status;
+const formatCreditStatus = (status) => creditStatusLabels[status] || status;
+
function Field({
label,
value,
@@ -78,7 +92,7 @@ function AuthPanel() {
return (
setMode((current) => (current === "login" ? "register" : "login"))}
>
- {mode === "login" ? "Konto erstellen" : "Bestehenden Account nutzen"}
+ {mode === "login" ? "Konto erstellen" : "Bestehendes Konto nutzen"}
@@ -270,7 +284,7 @@ function CartPanel() {
- Subtotal
+ Zwischensumme
{formatChf(cart.subtotal_cents)}
@@ -285,14 +299,14 @@ function CartPanel() {
{formatChf(discount.amount_cents)}
{" — "}
{discount.type === "discovery"
- ? "Discovery Set Credit für eine Full Size"
- : `Sample Credit für ${discount.slug}`}
+ ? "Discovery-Set-Gutschrift für einen 50-ml-Flakon"
+ : `Proben-Gutschrift für ${discount.slug}`}
))}
)}
- Total
+ Gesamt
{formatChf(cart.total_cents)}
@@ -370,14 +384,14 @@ function ProfilePanel() {
Profil
-
Hi, {user.first_name}.
+ Hallo, {user.first_name}.
- Logout
+ Abmelden
@@ -441,13 +455,13 @@ function ProfilePanel() {
- Discount Status
- {user.discoveryStatus}
+ Rabattstatus
+ {formatDiscoveryStatus(user.discoveryStatus)}
{user.sampleCredits?.length > 0 && (
{user.sampleCredits.map((credit) => (
- {credit.slug}: {credit.status}
+ {credit.slug}: {formatCreditStatus(credit.status)}
))}
@@ -455,7 +469,7 @@ function ProfilePanel() {
- Drop / Restock Preferences
+ Drop- und Verfügbarkeits-Einstellungen
{notificationLabels.map(([key, label]) => {
const active = !!notifications[key];
@@ -468,7 +482,7 @@ function ProfilePanel() {
onClick={() => togglePreference(key)}
>
{label}
-
{active ? "Active" : "Inactive"}
+
{active ? "Aktiv" : "Inaktiv"}
);
})}
@@ -499,13 +513,13 @@ function ProfilePanel() {
- Small Batch Access
+ Small-Batch-Zugang
{loyalty.unlocked ? "Freigeschaltet" : "Noch gesperrt"}
-
+
= 3}>
{loyalty.purchases}/3
@@ -559,7 +573,7 @@ function ReadBlock({ label, value }) {
function ShopDrawer() {
const { closePanel, panelOpen, panelType, user } = useShop();
- const drawerLabel = !user ? "Account" : panelType === "cart" ? "Warenkorb" : "Profil";
+ const drawerLabel = !user ? "Konto" : panelType === "cart" ? "Warenkorb" : "Profil";
return (
<>
diff --git a/parfum-shop/src/components/SupportChatbot.jsx b/parfum-shop/src/components/SupportChatbot.jsx
index 9500cc5..6f8b24a 100644
--- a/parfum-shop/src/components/SupportChatbot.jsx
+++ b/parfum-shop/src/components/SupportChatbot.jsx
@@ -24,7 +24,7 @@ const SUPPORT_TOPICS = {
title: "Fragen zum Discovery Set",
keywords: ["discovery", "set", "sample", "testen", "proben"],
reply:
- "Das Discovery Set ist dafür gedacht, mehrere Düfte in Ruhe auf der Haut zu testen, bevor du dich für eine Full Size entscheidest. So bekommst du ein genaueres Gefühl für Verlauf, Atmosphäre und Tragbarkeit der einzelnen Kompositionen.",
+ "Das Discovery Set ist dafür gedacht, mehrere Düfte in Ruhe auf der Haut zu testen, bevor du dich für einen 50-ml-Flakon entscheidest. So bekommst du ein genaueres Gefühl für Verlauf, Atmosphäre und Tragbarkeit der einzelnen Kompositionen.",
},
duftwahl: {
id: "duftwahl",
diff --git a/parfum-shop/src/pages/DiscoverySetPage.jsx b/parfum-shop/src/pages/DiscoverySetPage.jsx
index 1c46a66..e0d7524 100644
--- a/parfum-shop/src/pages/DiscoverySetPage.jsx
+++ b/parfum-shop/src/pages/DiscoverySetPage.jsx
@@ -9,20 +9,20 @@ const DISCOVERY_SET_IMAGE = "/atmos-discovery-set-thumbnail.webp";
const discoveryPanelFacts = [
{ label: "Umfang", value: "6 × 2ml" },
- { label: "Gutschrift", value: "CHF 48 werden beim späteren Full-Size-Kauf berücksichtigt." },
+ { label: "Gutschrift", value: "CHF 48 werden beim späteren 50-ml-Kauf berücksichtigt." },
];
const discoveryBenefits = [
{
- title: "6 × 2ml Samples aller Signature-Düfte",
+ title: "6 × 2-ml-Proben aller Signature-Düfte",
text: "Kalter Beton, Schwarzes Benzin, Verbranntes Chrom, Blasse Seide, Weisse Asche und Nasser Marmor.",
},
{
title: "CHF 48 Gutschein automatisch im Set",
- text: "Nur das erste Discovery Set erstellt den einmaligen Rabatt. Er wird bei einem späteren Full-Size-Kauf automatisch angerechnet.",
+ text: "Nur das erste Discovery Set erstellt den einmaligen Rabatt. Er wird bei einem späteren 50-ml-Kauf automatisch angerechnet.",
},
{
- title: "Jedes Sample = ca. 20 Anwendungen",
+ title: "Jede Probe = ca. 20 Anwendungen",
text: "Genug, um jeden Duft mehrere Tage im Alltag und in unterschiedlichen Situationen zu testen.",
},
{
@@ -45,7 +45,7 @@ const discoverySteps = [
{
number: "03",
title: "Entscheiden",
- text: "Full-Size bestellen. CHF 48 werden automatisch angerechnet, sofern der Rabatt noch nicht genutzt wurde.",
+ text: "50-ml-Flakon bestellen. CHF 48 werden automatisch angerechnet, sofern der Rabatt noch nicht genutzt wurde.",
},
];
@@ -53,7 +53,7 @@ const discoveryComparison = [
{
icon: "×",
title: "Traditioneller Weg",
- text: "CHF 180+ für eine Full Size ausgeben, ohne zu wissen, ob sie wirklich passt. Risiko: Fehlkauf, Überforderung oder ein Duft, der im Regal bleibt.",
+ text: "CHF 180+ für einen 50-ml-Flakon ausgeben, ohne zu wissen, ob er wirklich passt. Risiko: Fehlkauf, Überforderung oder ein Duft, der im Regal bleibt.",
},
{
icon: "✓",
@@ -88,7 +88,7 @@ function DiscoveryOrderPanel({ onBuy }) {
>
Kaufen
-
Nur das erste Set erstellt einen einmaligen CHF 48 Full-Size-Rabatt.
+
Nur das erste Set erstellt einen einmaligen CHF 48 Rabatt auf den 50-ml-Kauf.
);
@@ -105,7 +105,7 @@ function DiscoveryHero({ onBuy }) {
6 Düfte × 2ml. Jeden Duft eine Woche tragen. Verstehen, was
wirklich funktioniert. Ohne Risiko. Der sichere Einstieg in die
- Welt der Nischendüfte, bevor du dich für eine Full Size entscheidest.
+ Welt der Nischendüfte, bevor du dich für einen 50-ml-Flakon entscheidest.
@@ -215,7 +215,7 @@ function DiscoveryIncludedSection() {
@@ -276,7 +276,7 @@ function DiscoveryFinalCta({ onBuy }) {
Der sichere Einstieg.
- Kostenloser Versand · 2–3 Werktage · Einmalige Anrechnung bei Full-Size
+ Kostenloser Versand · 2–3 Werktage · Einmalige Anrechnung beim 50-ml-Kauf
@@ -296,13 +296,13 @@ function DiscoveryFinalCta({ onBuy }) {
function DiscoverySetPage() {
const { addToCart } = useShop();
const buyDiscoverySet = () =>
- addToCart("discovery-set", 1, "Discovery Set added.").catch(() => {});
+ addToCart("discovery-set", 1, "Discovery Set wurde in den Warenkorb gelegt.").catch(() => {});
return (
diff --git a/parfum-shop/src/pages/LandingPage.jsx b/parfum-shop/src/pages/LandingPage.jsx
index b28464c..24b694f 100644
--- a/parfum-shop/src/pages/LandingPage.jsx
+++ b/parfum-shop/src/pages/LandingPage.jsx
@@ -548,7 +548,7 @@ function LandingPage() {
DISCOVERY SET
- {"Alle 6 D\u00FCfte als 2ml Samples."}
+ {"Alle 6 D\u00FCfte als 2-ml-Proben."}
Jeden Duft eine Woche tragen.
diff --git a/parfum-shop/src/pages/SmallBatchPage.jsx b/parfum-shop/src/pages/SmallBatchPage.jsx
index 35b6e5a..7080178 100644
--- a/parfum-shop/src/pages/SmallBatchPage.jsx
+++ b/parfum-shop/src/pages/SmallBatchPage.jsx
@@ -31,7 +31,7 @@ function SmallBatchPage() {
})
.then(async (response) => {
const data = await response.json().catch(() => ({}));
- if (!response.ok) throw new Error(data.error || "Small Batch request failed.");
+ if (!response.ok) throw new Error(data.error || "Small-Batch-Anfrage fehlgeschlagen.");
setState({
loading: false,
error: "",
@@ -46,7 +46,7 @@ function SmallBatchPage() {
error:
error instanceof Error
? error.message
- : "Shop API unreachable. Start it with npm run dev and try again.",
+ : "Shop-API nicht erreichbar. Starte sie mit npm run dev und versuche es erneut.",
}));
});
}, [token, user?.loyaltyStatus]);
@@ -84,11 +84,11 @@ function SmallBatchPage() {
{!user ? (
- Login erforderlich
+ Anmeldung erforderlich
Melde dich an, um deinen Zugang zu prüfen.
- Small Batch Access wird aus deinen abgeschlossenen Bestellungen
+ Der Small-Batch-Zugang wird aus deinen abgeschlossenen Bestellungen
berechnet.
@@ -107,7 +107,7 @@ function SmallBatchPage() {
- Access Status
+ Zugangsstatus
{loyalty.unlocked ? "Freigeschaltet" : "Noch gesperrt"}
@@ -117,13 +117,13 @@ function SmallBatchPage() {
className={`small-status-pill ${loyalty.unlocked ? "is-unlocked" : ""}`}
data-reveal="fade"
>
- {loyalty.unlocked ? "Unlocked" : "Locked"}
+ {loyalty.unlocked ? "Freigeschaltet" : "Gesperrt"}
-
+
= 3}>
{loyalty.purchases}/3
@@ -134,7 +134,7 @@ function SmallBatchPage() {
{state.error &&
{state.error}
}
- {state.loading &&
Loading access…
}
+ {state.loading &&
Zugang wird geladen…
}
{loyalty.unlocked && (
{
try {
response = await fetch(path, { ...options, headers });
} catch {
- throw new Error("Shop API unreachable. Start it with npm run dev and try again.");
+ throw new Error("Shop-API nicht erreichbar. Starte sie mit npm run dev und versuche es erneut.");
}
const data = await response.json().catch(() => ({}));
if (!response.ok) {
- throw new Error(data.error || "Shop request failed.");
+ throw new Error(data.error || "Shop-Anfrage fehlgeschlagen.");
}
return data;
};
@@ -67,7 +67,7 @@ export function ShopProvider({ children }) {
try {
return await task();
} catch (err) {
- const message = err instanceof Error ? err.message : "Something went wrong.";
+ const message = err instanceof Error ? err.message : "Etwas ist schiefgelaufen.";
setError(message);
throw err;
} finally {
@@ -103,9 +103,9 @@ export function ShopProvider({ children }) {
applyState(data);
setPanelType("profile");
showToast({
- title: "Account created",
- message: "Your profile is ready.",
- actionLabel: "Profile",
+ title: "Konto erstellt",
+ message: "Dein Profil ist bereit.",
+ actionLabel: "Zum Profil",
actionPanel: "profile",
});
return data;
@@ -124,9 +124,9 @@ export function ShopProvider({ children }) {
applyState(data);
setPanelType("profile");
showToast({
- title: "Logged in",
- message: "Welcome back to atmos.",
- actionLabel: "Profile",
+ title: "Angemeldet",
+ message: "Willkommen zurück bei atmos.",
+ actionLabel: "Zum Profil",
actionPanel: "profile",
});
return data;
@@ -153,8 +153,8 @@ export function ShopProvider({ children }) {
discounts: [],
});
showToast({
- title: "Logged out",
- message: "Your session has ended.",
+ title: "Abgemeldet",
+ message: "Deine Sitzung wurde beendet.",
});
}),
[run, showToast, token]
@@ -179,7 +179,7 @@ export function ShopProvider({ children }) {
if (!token || !user) {
setPanelType("profile");
setPanelOpen(true);
- throw new Error("Please log in before adding products to the cart.");
+ throw new Error("Bitte melde dich an, bevor du Produkte in den Warenkorb legst.");
}
const data = await request(
"/api/cart/items",
@@ -188,9 +188,9 @@ export function ShopProvider({ children }) {
);
setCart(data.cart);
showToast({
- title: "Added to cart",
+ title: "Im Warenkorb",
message: itemMessage || data.message,
- actionLabel: "To the cart",
+ actionLabel: "Zum Warenkorb",
actionPanel: "cart",
});
return data;
@@ -236,9 +236,9 @@ export function ShopProvider({ children }) {
);
applyState(data);
showToast({
- title: "Checkout complete",
- message: "Your order was placed and your purchase history was updated.",
- actionLabel: "Profile",
+ title: "Bestellung abgeschlossen",
+ message: "Deine Bestellung wurde aufgegeben und deine Kaufhistorie aktualisiert.",
+ actionLabel: "Zum Profil",
actionPanel: "profile",
});
return data;
@@ -256,8 +256,8 @@ export function ShopProvider({ children }) {
);
setUser(data.user);
showToast({
- title: "Profile saved",
- message: "Your profile details were updated.",
+ title: "Profil gespeichert",
+ message: "Deine Profildaten wurden aktualisiert.",
});
return data;
}),
@@ -276,8 +276,8 @@ export function ShopProvider({ children }) {
current ? { ...current, notifications: data.notifications } : current
);
showToast({
- title: "Preferences saved",
- message: "Your drop and restock settings were updated.",
+ title: "Einstellungen gespeichert",
+ message: "Deine Drop- und Verfügbarkeits-Einstellungen wurden aktualisiert.",
});
return data;
}),
@@ -290,7 +290,7 @@ export function ShopProvider({ children }) {
if (!token || !user) {
setPanelType("profile");
setPanelOpen(true);
- throw new Error("Please log in to subscribe to restock updates.");
+ throw new Error("Bitte melde dich an, um Verfügbarkeits-Updates zu abonnieren.");
}
const data = await request(
"/api/product-subscriptions",
@@ -301,9 +301,9 @@ export function ShopProvider({ children }) {
current ? { ...current, productSubscriptions: data.subscriptions } : current
);
showToast({
- title: "Subscription saved",
- message: "You will receive updates for this product.",
- actionLabel: "Profile",
+ title: "Abo gespeichert",
+ message: "Du erhältst Updates zu diesem Produkt.",
+ actionLabel: "Zum Profil",
actionPanel: "profile",
});
return data;
@@ -323,8 +323,8 @@ export function ShopProvider({ children }) {
current ? { ...current, productSubscriptions: data.subscriptions } : current
);
showToast({
- title: "Subscription removed",
- message: "That restock update was deleted.",
+ title: "Abo entfernt",
+ message: "Dieses Verfügbarkeits-Update wurde gelöscht.",
});
return data;
}),