fix: slider
This commit is contained in:
@@ -16,6 +16,9 @@ import {
|
|||||||
Textarea,
|
Textarea,
|
||||||
} from "@/components/ui";
|
} from "@/components/ui";
|
||||||
import { EditIcon, PlusIcon, TrashIcon } from "@/components/icons";
|
import { EditIcon, PlusIcon, TrashIcon } from "@/components/icons";
|
||||||
|
import { ImagePicker } from "@/components/ImagePicker";
|
||||||
|
import { MediaPreview } from "@/components/MediaPreview";
|
||||||
|
import { pickUrl, IMAGE_KEYS } from "@/lib/media";
|
||||||
import { toFa } from "@/lib/utils";
|
import { toFa } from "@/lib/utils";
|
||||||
|
|
||||||
interface SliderAction {
|
interface SliderAction {
|
||||||
@@ -29,10 +32,12 @@ interface Slider {
|
|||||||
description?: string;
|
description?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
action?: SliderAction;
|
action?: SliderAction;
|
||||||
|
image_id?: number | null;
|
||||||
|
image?: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ACTION_TYPES = [
|
const ACTION_TYPES = [
|
||||||
{ value: "screen", label: "صفحه (screen)" },
|
{ value: "navigation", label: "صفحه (navigation)" },
|
||||||
{ value: "link", label: "لینک (link)" },
|
{ value: "link", label: "لینک (link)" },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -46,7 +51,9 @@ export default function SlidersPage() {
|
|||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
const [url, setUrl] = useState("");
|
const [url, setUrl] = useState("");
|
||||||
const [actionPath, setActionPath] = useState("");
|
const [actionPath, setActionPath] = useState("");
|
||||||
const [actionType, setActionType] = useState("screen");
|
const [actionType, setActionType] = useState("navigation");
|
||||||
|
const [imageId, setImageId] = useState<number | null>(null);
|
||||||
|
const [imageFile, setImageFile] = useState<File | null>(null);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
|
||||||
const [deleting, setDeleting] = useState<Slider | null>(null);
|
const [deleting, setDeleting] = useState<Slider | null>(null);
|
||||||
@@ -58,7 +65,9 @@ export default function SlidersPage() {
|
|||||||
setDescription("");
|
setDescription("");
|
||||||
setUrl("");
|
setUrl("");
|
||||||
setActionPath("");
|
setActionPath("");
|
||||||
setActionType("screen");
|
setActionType("navigation");
|
||||||
|
setImageId(null);
|
||||||
|
setImageFile(null);
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
}
|
}
|
||||||
function openEdit(row: Slider) {
|
function openEdit(row: Slider) {
|
||||||
@@ -67,7 +76,9 @@ export default function SlidersPage() {
|
|||||||
setDescription(row.description ?? "");
|
setDescription(row.description ?? "");
|
||||||
setUrl(row.url ?? "");
|
setUrl(row.url ?? "");
|
||||||
setActionPath(row.action?.path ?? "");
|
setActionPath(row.action?.path ?? "");
|
||||||
setActionType(row.action?.type ?? "screen");
|
setActionType(row.action?.type ?? "navigation");
|
||||||
|
setImageId(row.image_id ?? null);
|
||||||
|
setImageFile(null);
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,30 +86,22 @@ export default function SlidersPage() {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
if (editing) {
|
// Multipart for both (so an image file can be uploaded). Bracket keys
|
||||||
// Update is JSON on /slider/:id with a nested action object.
|
// build the nested action object; update uses POST /slider/:id.
|
||||||
await apiFetch(`/slider/${editing.id}`, {
|
const body = toFormData({
|
||||||
method: "PUT",
|
|
||||||
body: {
|
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
url,
|
url,
|
||||||
action: { path: actionPath, type: actionType },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
toast.success("اسلایدر ویرایش شد.");
|
|
||||||
} else {
|
|
||||||
// Create is multipart with literal bracket keys for the nested action.
|
|
||||||
await apiFetch("/slider", {
|
|
||||||
method: "POST",
|
|
||||||
body: toFormData({
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
"action[path]": actionPath,
|
"action[path]": actionPath,
|
||||||
"action[type]": actionType,
|
"action[type]": actionType,
|
||||||
url,
|
image_id: imageId,
|
||||||
}),
|
image: imageFile,
|
||||||
});
|
});
|
||||||
|
if (editing) {
|
||||||
|
await apiFetch(`/slider/${editing.id}`, { method: "POST", body });
|
||||||
|
toast.success("اسلایدر ویرایش شد.");
|
||||||
|
} else {
|
||||||
|
await apiFetch("/slider", { method: "POST", body });
|
||||||
toast.success("اسلایدر افزوده شد.");
|
toast.success("اسلایدر افزوده شد.");
|
||||||
}
|
}
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
@@ -127,6 +130,14 @@ export default function SlidersPage() {
|
|||||||
|
|
||||||
const columns: Column<Slider>[] = [
|
const columns: Column<Slider>[] = [
|
||||||
{ key: "id", header: "شناسه", render: (r) => toFa(r.id), className: "w-24" },
|
{ key: "id", header: "شناسه", render: (r) => toFa(r.id), className: "w-24" },
|
||||||
|
{
|
||||||
|
key: "image",
|
||||||
|
header: "تصویر",
|
||||||
|
className: "w-20",
|
||||||
|
render: (r) => (
|
||||||
|
<MediaPreview kind="image" src={pickUrl(r, IMAGE_KEYS)} label={r.title} />
|
||||||
|
),
|
||||||
|
},
|
||||||
{ key: "title", header: "عنوان", render: (r) => r.title ?? "—" },
|
{ key: "title", header: "عنوان", render: (r) => r.title ?? "—" },
|
||||||
{
|
{
|
||||||
key: "description",
|
key: "description",
|
||||||
@@ -224,6 +235,16 @@ export default function SlidersPage() {
|
|||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
|
<Field label="تصویر">
|
||||||
|
<ImagePicker
|
||||||
|
imageId={imageId}
|
||||||
|
onPickId={setImageId}
|
||||||
|
file={imageFile}
|
||||||
|
onPickFile={setImageFile}
|
||||||
|
existingUrl={editing ? pickUrl(editing, IMAGE_KEYS) : null}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<Field label="توضیحات">
|
<Field label="توضیحات">
|
||||||
<Textarea
|
<Textarea
|
||||||
value={description}
|
value={description}
|
||||||
|
|||||||
Reference in New Issue
Block a user