Show logo in the navbar, add image upload fields for logo/team/work/blog/gallery, and drive site chrome from settings everywhere. Co-authored-by: Cursor <cursoragent@cursor.com>
138 lines
4.6 KiB
Vue
138 lines
4.6 KiB
Vue
<template>
|
||
<div>
|
||
<h1>تنظیمات سایت</h1>
|
||
<p class="muted">این مقادیر روی هدر، فوتر و کل سایت اعمال میشوند.</p>
|
||
<form v-if="form" class="card" @submit.prevent="save">
|
||
<div class="grid-2">
|
||
<div class="field">
|
||
<label class="label">نام برند</label>
|
||
<input v-model="form.brandName" class="input" />
|
||
</div>
|
||
<div class="field">
|
||
<label class="label">تگلاین</label>
|
||
<input v-model="form.tagline" class="input" />
|
||
</div>
|
||
<div class="field">
|
||
<label class="label">ایمیل</label>
|
||
<input v-model="form.email" class="input" dir="ltr" />
|
||
</div>
|
||
<div class="field">
|
||
<label class="label">تلفن</label>
|
||
<input v-model="form.phone" class="input" dir="ltr" />
|
||
</div>
|
||
<div class="field">
|
||
<label class="label">آدرس</label>
|
||
<input v-model="form.address" class="input" />
|
||
</div>
|
||
<div class="field">
|
||
<label class="label">منطقه زمانی</label>
|
||
<select v-model="form.timezone" class="input" dir="ltr">
|
||
<option value="Asia/Tehran">Asia/Tehran (ایران)</option>
|
||
<option value="UTC">UTC</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<ImageField
|
||
v-model="form.logoUrl"
|
||
label="لوگو"
|
||
placeholder="/api/uploads/..."
|
||
hint="لوگو در هدر سایت نمایش داده میشود. اگر خالی باشد نام برند نشان داده میشود."
|
||
/>
|
||
|
||
<h2 style="margin: 1.25rem 0 0.75rem; font-size: 1rem">شبکههای اجتماعی</h2>
|
||
<div v-for="(s, i) in socials" :key="i" class="item-box">
|
||
<div class="row" style="justify-content: space-between; margin-bottom: 0.5rem">
|
||
<strong>مورد {{ i + 1 }}</strong>
|
||
<button type="button" class="btn danger" @click="socials.splice(i, 1)">حذف</button>
|
||
</div>
|
||
<div class="grid-2">
|
||
<div class="field">
|
||
<label class="label">نام</label>
|
||
<input v-model="s.label" class="input" />
|
||
</div>
|
||
<div class="field">
|
||
<label class="label">لینک</label>
|
||
<input v-model="s.url" class="input" dir="ltr" placeholder="https://..." />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<button type="button" class="btn ghost" @click="socials.push({ label: '', url: '' })">+ افزودن</button>
|
||
|
||
<p v-if="msg" class="muted" style="margin-top: 1rem">{{ msg }}</p>
|
||
<button class="btn" style="margin-top: 0.75rem">ذخیره</button>
|
||
</form>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { onMounted, reactive, ref } from "vue";
|
||
import { api } from "../lib/api";
|
||
import ImageField from "../components/ImageField.vue";
|
||
|
||
const form = ref<{
|
||
brandName: string;
|
||
tagline: string;
|
||
email: string;
|
||
phone: string;
|
||
address: string;
|
||
timezone: string;
|
||
logoUrl: string;
|
||
} | null>(null);
|
||
|
||
const socials = reactive<Array<{ label: string; url: string }>>([]);
|
||
const msg = ref("");
|
||
|
||
onMounted(async () => {
|
||
const { data } = await api.get("/admin/settings");
|
||
form.value = {
|
||
brandName: data.brandName || "",
|
||
tagline: data.tagline || "",
|
||
email: data.email || "",
|
||
phone: data.phone || "",
|
||
address: data.address || "",
|
||
timezone: data.timezone || "Asia/Tehran",
|
||
logoUrl: data.logoUrl || "",
|
||
};
|
||
socials.splice(0, socials.length, ...(Array.isArray(data.socials) ? data.socials : []));
|
||
if (!socials.length) socials.push({ label: "", url: "" });
|
||
});
|
||
|
||
async function save() {
|
||
if (!form.value) return;
|
||
msg.value = "";
|
||
try {
|
||
const payload = {
|
||
brandName: form.value.brandName,
|
||
tagline: form.value.tagline,
|
||
email: form.value.email,
|
||
phone: form.value.phone,
|
||
address: form.value.address,
|
||
timezone: form.value.timezone,
|
||
logoUrl: form.value.logoUrl || null,
|
||
socials: socials.filter((s) => s.label.trim() || s.url.trim()),
|
||
};
|
||
const { data } = await api.put("/admin/settings", payload);
|
||
form.value.logoUrl = data.logoUrl || "";
|
||
msg.value = "ذخیره شد. برای دیدن در سایت صفحه را رفرش کنید.";
|
||
} catch (e: any) {
|
||
const detail = e?.response?.data?.message;
|
||
msg.value = Array.isArray(detail)
|
||
? `خطا: ${detail.join("، ")}`
|
||
: detail
|
||
? `خطا: ${detail}`
|
||
: "خطا در ذخیره.";
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.item-box {
|
||
border: 1px solid var(--border);
|
||
border-radius: 10px;
|
||
padding: 0.9rem;
|
||
margin-bottom: 0.9rem;
|
||
background: #12151b;
|
||
}
|
||
</style>
|