feat: implement server-side pricing calculation and add site settings management
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -76,6 +76,7 @@
|
||||
<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="orders" role="tab"><span data-i18n="adminTabOrderHistory">Bestellungen</span> <span id="ordersBadge" class="tab-badge">0</span></button>
|
||||
<button class="tab" data-tab="vehicles" role="tab" data-i18n="adminVehicles">Fahrzeuge</button>
|
||||
<button class="tab" data-tab="settings" role="tab" data-i18n="adminSettings">Einstellungen</button>
|
||||
</div>
|
||||
|
||||
<!-- LEADS -->
|
||||
@@ -238,6 +239,22 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SETTINGS -->
|
||||
<div class="tab-panel" id="tab-settings" style="display:none;">
|
||||
<div class="panel" style="max-width:600px;">
|
||||
<h2 data-i18n="adminSettings">Einstellungen</h2>
|
||||
<div class="admin-form">
|
||||
<label>
|
||||
<span data-i18n="adminHeroImage">Hauptbild (Hero-Bereich)</span>
|
||||
<div class="admin-photo-preview" id="heroPreview" style="aspect-ratio:21/9;"></div>
|
||||
<input type="file" id="heroImageInput" accept="image/jpeg,image/png,image/webp" />
|
||||
</label>
|
||||
<p class="muted" style="font-size:0.82rem;" data-i18n="adminHeroImageHint">JPG/PNG/WebP. Wird als Hintergrundbild im Hero-Bereich der Website angezeigt.</p>
|
||||
<p class="form-feedback" id="heroFeedback"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Order detail dialog -->
|
||||
|
||||
@@ -177,6 +177,7 @@ const tabPanels = {
|
||||
customers: document.querySelector("#tab-customers"),
|
||||
orders: document.querySelector("#tab-orders"),
|
||||
vehicles: document.querySelector("#tab-vehicles"),
|
||||
settings: document.querySelector("#tab-settings"),
|
||||
};
|
||||
let activeTab = "leads";
|
||||
|
||||
@@ -206,6 +207,7 @@ function renderActiveTab() {
|
||||
if (activeTab === "customers") renderCustomers();
|
||||
if (activeTab === "orders") renderOrders();
|
||||
if (activeTab === "vehicles") renderVehicles();
|
||||
if (activeTab === "settings") renderSettings();
|
||||
}
|
||||
|
||||
// Sub-tabs (active/inactive leads)
|
||||
@@ -1100,4 +1102,47 @@ leadDialog.addEventListener("close", onDialogClose);
|
||||
customerDialog.addEventListener("close", onDialogClose);
|
||||
orderDialog.addEventListener("close", onDialogClose);
|
||||
|
||||
// =========================================================================
|
||||
// SETTINGS
|
||||
// =========================================================================
|
||||
const heroPreview = document.querySelector("#heroPreview");
|
||||
const heroImageInput = document.querySelector("#heroImageInput");
|
||||
const heroFeedback = document.querySelector("#heroFeedback");
|
||||
|
||||
async function renderSettings() {
|
||||
const { data } = await supabase.from("site_settings").select("value").eq("key", "hero_image_url").single();
|
||||
const url = data?.value || "/images/ferrari-main-car.png";
|
||||
heroPreview.style.backgroundImage = `url('${url}')`;
|
||||
}
|
||||
|
||||
heroImageInput.addEventListener("change", async () => {
|
||||
const file = heroImageInput.files?.[0];
|
||||
if (!file) return;
|
||||
heroFeedback.className = "form-feedback";
|
||||
heroFeedback.textContent = "Uploading...";
|
||||
try {
|
||||
const ext = (file.name.split(".").pop() || "jpg").toLowerCase();
|
||||
const path = `site/hero.${ext}`;
|
||||
const { error: upErr } = await supabase.storage
|
||||
.from("vehicle-photos")
|
||||
.upload(path, file, { contentType: file.type, upsert: true });
|
||||
if (upErr) throw upErr;
|
||||
const { data: pub } = supabase.storage.from("vehicle-photos").getPublicUrl(path);
|
||||
const publicUrl = pub.publicUrl;
|
||||
|
||||
// Save to site_settings
|
||||
const { error: dbErr } = await supabase
|
||||
.from("site_settings")
|
||||
.upsert({ key: "hero_image_url", value: publicUrl, updated_at: new Date().toISOString() }, { onConflict: "key" });
|
||||
if (dbErr) throw dbErr;
|
||||
|
||||
heroPreview.style.backgroundImage = `url('${publicUrl}')`;
|
||||
heroFeedback.className = "form-feedback";
|
||||
heroFeedback.textContent = "Gespeichert.";
|
||||
} catch (err) {
|
||||
heroFeedback.className = "form-feedback error";
|
||||
heroFeedback.textContent = err.message || String(err);
|
||||
}
|
||||
});
|
||||
|
||||
bootstrap();
|
||||
|
||||
@@ -522,3 +522,11 @@ langToggle.textContent = getLang() === "de" ? "EN" : "DE";
|
||||
applyI18n();
|
||||
renderReviews();
|
||||
loadVehicles();
|
||||
|
||||
// Load hero image from site_settings
|
||||
(async () => {
|
||||
const { data } = await supabase.from("site_settings").select("value").eq("key", "hero_image_url").single();
|
||||
if (data && data.value) {
|
||||
document.querySelector(".hero").style.setProperty("--hero-bg", `url('${data.value}')`);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -130,6 +130,9 @@ export const translations = {
|
||||
adminLeads: "Leads",
|
||||
adminCustomers: "Kunden",
|
||||
adminVehicles: "Fahrzeuge",
|
||||
adminSettings: "Einstellungen",
|
||||
adminHeroImage: "Hauptbild (Hero-Bereich)",
|
||||
adminHeroImageHint: "JPG/PNG/WebP. Wird als Hintergrundbild im Hero-Bereich der Website angezeigt.",
|
||||
adminNewVehicle: "Neues Fahrzeug",
|
||||
adminAllVehicles: "Alle Fahrzeuge",
|
||||
adminPhotoUpload: "Foto hochladen (JPG/PNG/WebP, max 50 MB)",
|
||||
@@ -358,6 +361,9 @@ export const translations = {
|
||||
adminLeads: "Leads",
|
||||
adminCustomers: "Customers",
|
||||
adminVehicles: "Vehicles",
|
||||
adminSettings: "Settings",
|
||||
adminHeroImage: "Main Photo (Hero Section)",
|
||||
adminHeroImageHint: "JPG/PNG/WebP. Displayed as the background image in the hero section of the website.",
|
||||
adminNewVehicle: "New vehicle",
|
||||
adminAllVehicles: "All vehicles",
|
||||
adminPhotoUpload: "Upload photo (JPG/PNG/WebP, max 50 MB)",
|
||||
|
||||
+1
-1
@@ -246,7 +246,7 @@ section { padding: 5rem 0; }
|
||||
inset: 0;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(11,12,16,0.6) 0%, rgba(11,12,16,0.95) 100%),
|
||||
url('images/ferrari-main-car.png') center / cover no-repeat;
|
||||
var(--hero-bg, url('images/ferrari-main-car.png')) center / cover no-repeat;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user