feat(admin): replace checkbox with toggle-switch slider, add i18n multilanguage
This commit is contained in:
+48
-44
@@ -61,19 +61,20 @@
|
|||||||
<div class="admin-bar">
|
<div class="admin-bar">
|
||||||
<h1>MC Cars · Admin</h1>
|
<h1>MC Cars · Admin</h1>
|
||||||
<div style="display:flex;gap:0.6rem;align-items:center;flex-wrap:wrap;">
|
<div style="display:flex;gap:0.6rem;align-items:center;flex-wrap:wrap;">
|
||||||
<a href="index.html" class="btn ghost small">Website</a>
|
<a href="index.html" class="btn ghost small" data-i18n="adminNavWebsite">Website</a>
|
||||||
|
<button class="lang-toggle" type="button" aria-label="Sprache wechseln" style="margin-left:auto;">EN</button>
|
||||||
|
|
||||||
<span id="adminWho" style="color:var(--muted);font-size:0.85rem;"></span>
|
<span id="adminWho" style="color:var(--muted);font-size:0.85rem;margin-left:1rem;"></span>
|
||||||
<button id="changePwBtn" class="btn ghost small">Passwort aendern</button>
|
<button id="changePwBtn" class="btn ghost small" data-i18n="adminChangePw">Passwort aendern</button>
|
||||||
<button id="logoutBtn" class="btn small">Logout</button>
|
<button id="logoutBtn" class="btn small" data-i18n="adminLogout">Logout</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tabs -->
|
<!-- Tabs -->
|
||||||
<div class="admin-tabs" role="tablist">
|
<div class="admin-tabs" role="tablist">
|
||||||
<button class="tab active" data-tab="leads" role="tab">Leads <span id="leadsBadge" class="tab-badge">0</span></button>
|
<button class="tab active" data-tab="leads" role="tab"><span data-i18n="adminLeads">Leads</span> <span id="leadsBadge" class="tab-badge">0</span></button>
|
||||||
<button class="tab" data-tab="customers" role="tab">Kunden <span id="customersBadge" class="tab-badge">0</span></button>
|
<button class="tab" data-tab="customers" role="tab"><span data-i18n="adminCustomers">Kunden</span> <span id="customersBadge" class="tab-badge">0</span></button>
|
||||||
<button class="tab" data-tab="vehicles" role="tab">Fahrzeuge</button>
|
<button class="tab" data-tab="vehicles" role="tab" data-i18n="adminVehicles">Fahrzeuge</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- LEADS -->
|
<!-- LEADS -->
|
||||||
@@ -82,18 +83,18 @@
|
|||||||
<div style="display:flex;justify-content:space-between;align-items:center;gap:1rem;flex-wrap:wrap;margin-bottom:1rem;">
|
<div style="display:flex;justify-content:space-between;align-items:center;gap:1rem;flex-wrap:wrap;margin-bottom:1rem;">
|
||||||
<h2 style="margin:0;">Leads</h2>
|
<h2 style="margin:0;">Leads</h2>
|
||||||
<div class="sub-tabs" role="tablist">
|
<div class="sub-tabs" role="tablist">
|
||||||
<button class="sub-tab active" data-lview="active">Aktive Leads</button>
|
<button class="sub-tab active" data-lview="active" data-i18n="adminActiveLeads">Aktive Leads</button>
|
||||||
<button class="sub-tab" data-lview="inactive">Abgeschlossen</button>
|
<button class="sub-tab" data-lview="inactive" data-i18n="adminClosedLeads">Abgeschlossen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="admin-table" id="leadsTable">
|
<table class="admin-table" id="leadsTable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Eingang</th>
|
<th data-i18n="adminReceived">Eingang</th>
|
||||||
<th>Name / E-Mail</th>
|
<th data-i18n="adminNameEmail">Name / E-Mail</th>
|
||||||
<th>Fahrzeug</th>
|
<th data-i18n="adminVehicleTab">Fahrzeug</th>
|
||||||
<th>Zeitraum</th>
|
<th data-i18n="adminPeriod">Zeitraum</th>
|
||||||
<th>Status</th>
|
<th data-i18n="adminStatus">Status</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -111,11 +112,11 @@
|
|||||||
<table class="admin-table" id="customersTable">
|
<table class="admin-table" id="customersTable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Erster Kontakt</th>
|
<th data-i18n="adminFirstContact">Erster Kontakt</th>
|
||||||
<th>Name / E-Mail</th>
|
<th data-i18n="adminNameEmail">Name / E-Mail</th>
|
||||||
<th>Telefon</th>
|
<th data-i18n="adminPhone">Telefon</th>
|
||||||
<th>Quelle (Lead)</th>
|
<th data-i18n="adminSourceLead">Quelle (Lead)</th>
|
||||||
<th>Status</th>
|
<th data-i18n="adminStatus">Status</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -129,73 +130,76 @@
|
|||||||
<div class="tab-panel" id="tab-vehicles" style="display:none;">
|
<div class="tab-panel" id="tab-vehicles" style="display:none;">
|
||||||
<div class="admin-grid">
|
<div class="admin-grid">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<h2 id="formTitle">Neues Fahrzeug</h2>
|
<h2 id="formTitle" data-i18n="adminNewVehicle">Neues Fahrzeug</h2>
|
||||||
<form class="admin-form" id="vehicleForm">
|
<form class="admin-form" id="vehicleForm">
|
||||||
<input type="hidden" name="vid" />
|
<input type="hidden" name="vid" />
|
||||||
|
|
||||||
<div class="admin-photo-preview" id="photoPreview"></div>
|
<div class="admin-photo-preview" id="photoPreview"></div>
|
||||||
<label>
|
<label>
|
||||||
<span>Foto hochladen (JPG/PNG/WebP, max 50 MB)</span>
|
<span data-i18n="adminPhotoUpload">Foto hochladen (JPG/PNG/WebP, max 50 MB)</span>
|
||||||
<input type="file" id="photoInput" accept="image/*" />
|
<input type="file" id="photoInput" accept="image/*" />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>Foto-URL (wird automatisch gesetzt nach Upload)</span>
|
<span data-i18n="adminPhotoUrl">Foto-URL (wird automatisch gesetzt nach Upload)</span>
|
||||||
<input type="url" name="photo_url" placeholder="https://..." />
|
<input type="url" name="photo_url" placeholder="https://..." />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="row2">
|
<div class="row2">
|
||||||
<label><span>Marke</span><input name="brand" required /></label>
|
<label><span data-i18n="adminBrand">Marke</span><input name="brand" required /></label>
|
||||||
<label><span>Modell</span><input name="model" required /></label>
|
<label><span data-i18n="adminModel">Modell</span><input name="model" required /></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row3">
|
<div class="row3">
|
||||||
<label><span>PS</span><input type="number" name="power_hp" min="0" /></label>
|
<label><span data-i18n="adminPower">PS</span><input type="number" name="power_hp" min="0" /></label>
|
||||||
<label><span>Top-Speed km/h</span><input type="number" name="top_speed_kmh" min="0" /></label>
|
<label><span data-i18n="adminSpeed">Top-Speed km/h</span><input type="number" name="top_speed_kmh" min="0" /></label>
|
||||||
<label><span>0-100</span><input name="acceleration" placeholder="3.2s" /></label>
|
<label><span data-i18n="adminAccel">0-100</span><input name="acceleration" placeholder="3.2s" /></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row3">
|
<div class="row3">
|
||||||
<label><span>Sitze</span><input type="number" name="seats" min="1" value="2" /></label>
|
<label><span data-i18n="adminSeats">Sitze</span><input type="number" name="seats" min="1" value="2" /></label>
|
||||||
<label><span>Preis / Tag (€)</span><input type="number" name="daily_price_eur" min="0" required /></label>
|
<label><span data-i18n="adminPrice">Preis / Tag (€)</span><input type="number" name="daily_price_eur" min="0" required /></label>
|
||||||
<label><span>Reihenfolge</span><input type="number" name="sort_order" value="100" /></label>
|
<label><span data-i18n="adminSort">Reihenfolge</span><input type="number" name="sort_order" value="100" /></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<span>Standort</span>
|
<span data-i18n="adminLocation">Standort</span>
|
||||||
<input name="location" value="Steiermark (TBD)" />
|
<input name="location" value="Steiermark (TBD)" />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<span>Beschreibung (Deutsch)</span>
|
<span data-i18n="adminDescDe">Beschreibung (Deutsch)</span>
|
||||||
<textarea name="description_de" rows="3"></textarea>
|
<textarea name="description_de" rows="3"></textarea>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>Description (English)</span>
|
<span data-i18n="adminDescEn">Description (English)</span>
|
||||||
<textarea name="description_en" rows="3"></textarea>
|
<textarea name="description_en" rows="3"></textarea>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label style="flex-direction:row;align-items:center;gap:0.5rem;">
|
<label style="flex-direction:row;align-items:center;gap:0.8rem;margin-top:0.5rem;cursor:pointer;">
|
||||||
<input type="checkbox" name="is_active" checked style="width:auto;" />
|
<div class="toggle-switch">
|
||||||
<span>Aktiv / auf Website sichtbar</span>
|
<input type="checkbox" name="is_active" id="isActiveCheck" checked />
|
||||||
|
<span class="toggle-slider"></span>
|
||||||
|
</div>
|
||||||
|
<span data-i18n="adminActiveVisible" style="user-select:none;">Aktiv / auf Website sichtbar</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div style="display:flex;gap:0.5rem;">
|
<div style="display:flex;gap:0.5rem;margin-top:1rem;">
|
||||||
<button class="btn" type="submit" id="saveBtn">Speichern</button>
|
<button class="btn" type="submit" id="saveBtn" data-i18n="adminSave">Speichern</button>
|
||||||
<button class="btn ghost" type="button" id="resetBtn">Neu</button>
|
<button class="btn ghost" type="button" id="resetBtn" data-i18n="adminReset">Neu</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="form-feedback" id="formFeedback"></p>
|
<p class="form-feedback" id="formFeedback"></p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<h2>Alle Fahrzeuge</h2>
|
<h2 data-i18n="adminAllVehicles">Alle Fahrzeuge</h2>
|
||||||
<table class="admin-table" id="adminTable">
|
<table class="admin-table" id="adminTable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Foto</th>
|
<th data-i18n="adminPhoto">Foto</th>
|
||||||
<th>Marke / Modell</th>
|
<th data-i18n="adminBrandTable">Marke / Modell</th>
|
||||||
<th>€ / Tag</th>
|
<th data-i18n="adminPriceTable">€ / Tag</th>
|
||||||
<th>Aktiv</th>
|
<th data-i18n="adminActive">Aktiv</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|||||||
+27
-10
@@ -1,4 +1,5 @@
|
|||||||
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.45.4";
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.45.4";
|
||||||
|
import { getLang, setLang, t, applyI18n } from "./i18n.js";
|
||||||
|
|
||||||
const SUPA_URL = window.MCCARS_CONFIG?.SUPABASE_URL ?? "";
|
const SUPA_URL = window.MCCARS_CONFIG?.SUPABASE_URL ?? "";
|
||||||
const SUPA_KEY = window.MCCARS_CONFIG?.SUPABASE_ANON_KEY || "";
|
const SUPA_KEY = window.MCCARS_CONFIG?.SUPABASE_ANON_KEY || "";
|
||||||
@@ -12,6 +13,7 @@ const supabase = createClient(SUPA_URL, SUPA_KEY, {
|
|||||||
// ----- DOM -----
|
// ----- DOM -----
|
||||||
const loginView = document.querySelector("#loginView");
|
const loginView = document.querySelector("#loginView");
|
||||||
const adminView = document.querySelector("#adminView");
|
const adminView = document.querySelector("#adminView");
|
||||||
|
const langToggle = document.querySelector(".lang-toggle");
|
||||||
const rotateView = document.querySelector("#rotateView");
|
const rotateView = document.querySelector("#rotateView");
|
||||||
const loginForm = document.querySelector("#loginForm");
|
const loginForm = document.querySelector("#loginForm");
|
||||||
const loginError = document.querySelector("#loginError");
|
const loginError = document.querySelector("#loginError");
|
||||||
@@ -59,6 +61,7 @@ const state = {
|
|||||||
// AUTH FLOW
|
// AUTH FLOW
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
|
applyI18n();
|
||||||
const { data: { session } } = await supabase.auth.getSession();
|
const { data: { session } } = await supabase.auth.getSession();
|
||||||
if (session) {
|
if (session) {
|
||||||
// Always fetch fresh user from server so metadata (must_change_password) is current.
|
// Always fetch fresh user from server so metadata (must_change_password) is current.
|
||||||
@@ -204,8 +207,8 @@ function renderVehicles() {
|
|||||||
<td>€ ${v.daily_price_eur}</td>
|
<td>€ ${v.daily_price_eur}</td>
|
||||||
<td>${v.is_active ? "✅" : "—"}</td>
|
<td>${v.is_active ? "✅" : "—"}</td>
|
||||||
<td style="white-space:nowrap;">
|
<td style="white-space:nowrap;">
|
||||||
<button class="btn small ghost" data-edit="${v.id}">Edit</button>
|
<button class="btn small ghost" data-edit="${v.id}">${t("editVehicle")}</button>
|
||||||
<button class="btn small danger" data-del="${v.id}">Del</button>
|
<button class="btn small danger" data-del="${v.id}">${t("adminDel")}</button>
|
||||||
</td>`;
|
</td>`;
|
||||||
tableBody.appendChild(tr);
|
tableBody.appendChild(tr);
|
||||||
}
|
}
|
||||||
@@ -352,12 +355,12 @@ function renderLeads() {
|
|||||||
<td>${esc(l.date_from || "—")} → ${esc(l.date_to || "—")}</td>
|
<td>${esc(l.date_from || "—")} → ${esc(l.date_to || "—")}</td>
|
||||||
<td><span class="pill pill-${esc(l.status)}">${esc(l.status)}</span></td>
|
<td><span class="pill pill-${esc(l.status)}">${esc(l.status)}</span></td>
|
||||||
<td style="white-space:nowrap;">
|
<td style="white-space:nowrap;">
|
||||||
<button class="btn small ghost" data-open="${l.id}">Details</button>
|
<button class="btn small ghost" data-open="${l.id}">${t("adminDetails")}</button>
|
||||||
${wantActive ? `
|
${wantActive ? `
|
||||||
<button class="btn small" data-qual="${l.id}">Qualifizieren</button>
|
<button class="btn small" data-qual="${l.id}">${t("adminQualify")}</button>
|
||||||
<button class="btn small danger" data-disq="${l.id}">Ablehnen</button>
|
<button class="btn small danger" data-disq="${l.id}">${t("adminReject")}</button>
|
||||||
` : `
|
` : `
|
||||||
<button class="btn small ghost" data-reopen="${l.id}">Wieder oeffnen</button>
|
<button class="btn small ghost" data-reopen="${l.id}">${t("adminReopen")}</button>
|
||||||
`}
|
`}
|
||||||
</td>`;
|
</td>`;
|
||||||
leadsTableBody.appendChild(tr);
|
leadsTableBody.appendChild(tr);
|
||||||
@@ -385,9 +388,9 @@ function openLead(id) {
|
|||||||
</dl>
|
</dl>
|
||||||
<div style="display:flex;gap:0.5rem;justify-content:flex-end;margin-top:0.8rem;">
|
<div style="display:flex;gap:0.5rem;justify-content:flex-end;margin-top:0.8rem;">
|
||||||
${l.is_active ? `
|
${l.is_active ? `
|
||||||
<button class="btn danger" id="dlgDisq">Ablehnen</button>
|
<button class="btn danger" id="dlgDisq">${t("adminReject")}</button>
|
||||||
<button class="btn" id="dlgQual">Qualifizieren</button>
|
<button class="btn" id="dlgQual">${t("adminQualify")}</button>
|
||||||
` : `<button class="btn ghost" id="dlgReopen">Wieder oeffnen</button>`}
|
` : `<button class="btn ghost" id="dlgReopen">${t("adminReopen")}</button>`}
|
||||||
</div>`;
|
</div>`;
|
||||||
leadDialog.showModal();
|
leadDialog.showModal();
|
||||||
const note = () => document.querySelector("#leadNote").value;
|
const note = () => document.querySelector("#leadNote").value;
|
||||||
@@ -448,7 +451,7 @@ function renderCustomers() {
|
|||||||
<td><span class="pill pill-${esc(c.status)}">${esc(c.status)}</span></td>
|
<td><span class="pill pill-${esc(c.status)}">${esc(c.status)}</span></td>
|
||||||
<td style="white-space:nowrap;">
|
<td style="white-space:nowrap;">
|
||||||
<button class="btn small ghost" data-toggle="${c.id}" data-status="${c.status}">
|
<button class="btn small ghost" data-toggle="${c.id}" data-status="${c.status}">
|
||||||
${c.status === "active" ? "Inaktiv setzen" : "Aktiv setzen"}
|
${c.status === "active" ? t("adminSetInactive") : t("adminSetActive")}
|
||||||
</button>
|
</button>
|
||||||
</td>`;
|
</td>`;
|
||||||
customersTableBody.appendChild(tr);
|
customersTableBody.appendChild(tr);
|
||||||
@@ -493,4 +496,18 @@ function fmtDate(iso) {
|
|||||||
return d.toLocaleString("de-AT", { dateStyle: "short", timeStyle: "short" });
|
return d.toLocaleString("de-AT", { dateStyle: "short", timeStyle: "short" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (langToggle) {
|
||||||
|
langToggle.addEventListener("click", () => {
|
||||||
|
const current = getLang();
|
||||||
|
setLang(current === "de" ? "en" : "de");
|
||||||
|
langToggle.textContent = getLang() === "de" ? "EN" : "DE";
|
||||||
|
applyI18n();
|
||||||
|
// Re-render JS injected text correctly
|
||||||
|
if (state.vehicles) renderVehicles();
|
||||||
|
if (state.leads) renderLeads();
|
||||||
|
if (state.customers) renderCustomers();
|
||||||
|
});
|
||||||
|
langToggle.textContent = getLang() === "de" ? "EN" : "DE";
|
||||||
|
}
|
||||||
|
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|||||||
+94
-2
@@ -69,11 +69,57 @@ export const translations = {
|
|||||||
footerNav: "Navigation",
|
footerNav: "Navigation",
|
||||||
imprint: "Impressum",
|
imprint: "Impressum",
|
||||||
privacy: "Datenschutz",
|
privacy: "Datenschutz",
|
||||||
terms: "Mietbedingungen",
|
footerTerms: "Mietbedingungen",
|
||||||
copyright: "Alle Rechte vorbehalten.",
|
copyright: "Alle Rechte vorbehalten.",
|
||||||
|
|
||||||
close: "Schliessen",
|
close: "Schliessen",
|
||||||
editVehicle: "Fahrzeug bearbeiten",
|
editVehicle: "Fahrzeug bearbeiten",
|
||||||
|
|
||||||
|
adminNavWebsite: "Website",
|
||||||
|
adminChangePw: "Passwort aendern",
|
||||||
|
adminLogout: "Logout",
|
||||||
|
adminLeads: "Leads",
|
||||||
|
adminCustomers: "Kunden",
|
||||||
|
adminVehicles: "Fahrzeuge",
|
||||||
|
adminNewVehicle: "Neues Fahrzeug",
|
||||||
|
adminAllVehicles: "Alle Fahrzeuge",
|
||||||
|
adminPhotoUpload: "Foto hochladen (JPG/PNG/WebP, max 50 MB)",
|
||||||
|
adminPhotoUrl: "Foto-URL (wird automatisch gesetzt nach Upload)",
|
||||||
|
adminBrand: "Marke",
|
||||||
|
adminModel: "Modell",
|
||||||
|
adminPower: "PS",
|
||||||
|
adminSpeed: "Top-Speed km/h",
|
||||||
|
adminAccel: "0-100",
|
||||||
|
adminSeats: "Sitze",
|
||||||
|
adminPrice: "Preis / Tag (€)",
|
||||||
|
adminSort: "Reihenfolge",
|
||||||
|
adminLocation: "Standort",
|
||||||
|
adminDescDe: "Beschreibung (Deutsch)",
|
||||||
|
adminDescEn: "Description (English)",
|
||||||
|
adminActiveVisible: "Aktiv / auf Website sichtbar",
|
||||||
|
adminSave: "Speichern",
|
||||||
|
adminReset: "Neu",
|
||||||
|
adminPhoto: "Foto",
|
||||||
|
adminBrandTable: "Marke / Modell",
|
||||||
|
adminPriceTable: "€ / Tag",
|
||||||
|
adminActive: "Aktiv",
|
||||||
|
adminDel: "Löschen",
|
||||||
|
adminQualify: "Qualifizieren",
|
||||||
|
adminReject: "Ablehnen",
|
||||||
|
adminReopen: "Wieder öffnen",
|
||||||
|
adminDetails: "Details",
|
||||||
|
adminSetInactive: "Inaktiv setzen",
|
||||||
|
adminSetActive: "Aktiv setzen",
|
||||||
|
adminActiveLeads: "Aktive Leads",
|
||||||
|
adminClosedLeads: "Abgeschlossen",
|
||||||
|
adminSourceLead: "Quelle (Lead)",
|
||||||
|
adminFirstContact: "Erster Kontakt",
|
||||||
|
adminNameEmail: "Name / E-Mail",
|
||||||
|
adminPhone: "Telefon",
|
||||||
|
adminStatus: "Status",
|
||||||
|
adminReceived: "Eingang",
|
||||||
|
adminVehicleTab: "Fahrzeug",
|
||||||
|
adminPeriod: "Zeitraum",
|
||||||
},
|
},
|
||||||
en: {
|
en: {
|
||||||
navCars: "Fleet",
|
navCars: "Fleet",
|
||||||
@@ -144,11 +190,57 @@ export const translations = {
|
|||||||
footerNav: "Navigation",
|
footerNav: "Navigation",
|
||||||
imprint: "Imprint",
|
imprint: "Imprint",
|
||||||
privacy: "Privacy",
|
privacy: "Privacy",
|
||||||
terms: "Rental conditions",
|
footerTerms: "Rental conditions",
|
||||||
copyright: "All rights reserved.",
|
copyright: "All rights reserved.",
|
||||||
|
|
||||||
close: "Close",
|
close: "Close",
|
||||||
editVehicle: "Edit vehicle",
|
editVehicle: "Edit vehicle",
|
||||||
|
|
||||||
|
adminNavWebsite: "Website",
|
||||||
|
adminChangePw: "Change password",
|
||||||
|
adminLogout: "Logout",
|
||||||
|
adminLeads: "Leads",
|
||||||
|
adminCustomers: "Customers",
|
||||||
|
adminVehicles: "Vehicles",
|
||||||
|
adminNewVehicle: "New vehicle",
|
||||||
|
adminAllVehicles: "All vehicles",
|
||||||
|
adminPhotoUpload: "Upload photo (JPG/PNG/WebP, max 50 MB)",
|
||||||
|
adminPhotoUrl: "Photo URL (auto-set after upload)",
|
||||||
|
adminBrand: "Brand",
|
||||||
|
adminModel: "Model",
|
||||||
|
adminPower: "HP",
|
||||||
|
adminSpeed: "Top speed km/h",
|
||||||
|
adminAccel: "0-62",
|
||||||
|
adminSeats: "Seats",
|
||||||
|
adminPrice: "Price / day (€)",
|
||||||
|
adminSort: "Sort order",
|
||||||
|
adminLocation: "Location",
|
||||||
|
adminDescDe: "Description (German)",
|
||||||
|
adminDescEn: "Description (English)",
|
||||||
|
adminActiveVisible: "Active / visible on website",
|
||||||
|
adminSave: "Save",
|
||||||
|
adminReset: "New",
|
||||||
|
adminPhoto: "Photo",
|
||||||
|
adminBrandTable: "Brand / Model",
|
||||||
|
adminPriceTable: "€ / day",
|
||||||
|
adminActive: "Active",
|
||||||
|
adminDel: "Delete",
|
||||||
|
adminQualify: "Qualify",
|
||||||
|
adminReject: "Reject",
|
||||||
|
adminReopen: "Reopen",
|
||||||
|
adminDetails: "Details",
|
||||||
|
adminSetInactive: "Set inactive",
|
||||||
|
adminSetActive: "Set active",
|
||||||
|
adminActiveLeads: "Active leads",
|
||||||
|
adminClosedLeads: "Closed",
|
||||||
|
adminSourceLead: "Source (Lead)",
|
||||||
|
adminFirstContact: "First contact",
|
||||||
|
adminNameEmail: "Name / Email",
|
||||||
|
adminPhone: "Phone",
|
||||||
|
adminStatus: "Status",
|
||||||
|
adminReceived: "Received",
|
||||||
|
adminVehicleTab: "Vehicle",
|
||||||
|
adminPeriod: "Period",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -604,6 +604,49 @@ table.admin-table tbody tr:hover {
|
|||||||
filter: brightness(1.1);
|
filter: brightness(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------------- Forms / Toggle Switch ---------------- */
|
||||||
|
.toggle-switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 44px;
|
||||||
|
height: 24px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.toggle-switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
.toggle-slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0; left: 0; right: 0; bottom: 0;
|
||||||
|
background-color: var(--line);
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
.toggle-slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
left: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
background-color: var(--text);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
input:checked + .toggle-slider {
|
||||||
|
background-color: var(--accent);
|
||||||
|
}
|
||||||
|
input:focus + .toggle-slider {
|
||||||
|
box-shadow: 0 0 0 2px var(--bg-card), 0 0 0 4px var(--accent);
|
||||||
|
}
|
||||||
|
input:checked + .toggle-slider:before {
|
||||||
|
transform: translateX(20px);
|
||||||
|
background-color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
/* Admin tabs */
|
/* Admin tabs */
|
||||||
.admin-tabs {
|
.admin-tabs {
|
||||||
display: flex; gap: 0.4rem;
|
display: flex; gap: 0.4rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user