|
|
|
|
|
import { create } from "zustand"; |
|
|
|
|
|
|
|
|
const postJSON = async (url, body) => { |
|
|
const res = await fetch(url, { |
|
|
method: "POST", |
|
|
headers: { "Content-Type": "application/json" }, |
|
|
body: JSON.stringify(body), |
|
|
}); |
|
|
if (!res.ok) throw new Error(await res.text()); |
|
|
return res.json(); |
|
|
}; |
|
|
|
|
|
const postFile = async (url, file) => { |
|
|
const fd = new FormData(); |
|
|
fd.append("file", file); |
|
|
const res = await fetch(url, { method: "POST", body: fd }); |
|
|
if (!res.ok) throw new Error(await res.text()); |
|
|
return res.json(); |
|
|
}; |
|
|
|
|
|
const createInitialDetail = () => ({ |
|
|
previewUrl: null, |
|
|
transform: { scale: 1, tx: 0, ty: 0 }, |
|
|
tile: { enabled: false }, |
|
|
}); |
|
|
|
|
|
const useStore = create((set, get) => ({ |
|
|
|
|
|
model: "MT", |
|
|
active: "front", |
|
|
details: { |
|
|
front: createInitialDetail(), |
|
|
sleeveL: createInitialDetail(), |
|
|
back: createInitialDetail(), |
|
|
sleeveR: createInitialDetail(), |
|
|
}, |
|
|
uploadedPath: null, |
|
|
uploadedUrl: null, |
|
|
busy: false, |
|
|
lastOrderInfo: null, |
|
|
|
|
|
|
|
|
setModel: (model) => set({ model }), |
|
|
setActive: (active) => set({ active }), |
|
|
setScale: (scale) => |
|
|
set((state) => ({ |
|
|
details: { |
|
|
...state.details, |
|
|
[state.active]: { |
|
|
...state.details[state.active], |
|
|
transform: { |
|
|
...state.details[state.active].transform, |
|
|
scale, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
})), |
|
|
setTx: (tx) => |
|
|
set((state) => ({ |
|
|
details: { |
|
|
...state.details, |
|
|
[state.active]: { |
|
|
...state.details[state.active], |
|
|
transform: { |
|
|
...state.details[state.active].transform, |
|
|
tx, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
})), |
|
|
setTy: (ty) => |
|
|
set((state) => ({ |
|
|
details: { |
|
|
...state.details, |
|
|
[state.active]: { |
|
|
...state.details[state.active], |
|
|
transform: { |
|
|
...state.details[state.active].transform, |
|
|
ty, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
})), |
|
|
toggleTile: () => |
|
|
set((state) => ({ |
|
|
details: { |
|
|
...state.details, |
|
|
[state.active]: { |
|
|
...state.details[state.active], |
|
|
tile: { |
|
|
enabled: !state.details[state.active].tile.enabled, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
})), |
|
|
|
|
|
|
|
|
async upload(file) { |
|
|
set({ busy: true }); |
|
|
try { |
|
|
const { path, url } = await postFile("/api/upload", file); |
|
|
set({ uploadedPath: path, uploadedUrl: url }); |
|
|
return path; |
|
|
} finally { |
|
|
set({ busy: false }); |
|
|
} |
|
|
}, |
|
|
|
|
|
|
|
|
async spread() { |
|
|
const { model, active, details, uploadedPath } = get(); |
|
|
const detail = details[active]; |
|
|
if (!uploadedPath) throw new Error("Сначала загрузите принт"); |
|
|
|
|
|
const detail = details[active]; |
|
|
set({ busy: true }); |
|
|
try { |
|
|
const payload = { |
|
|
model, |
|
|
view: active, |
|
|
details: { |
|
|
print_path: uploadedPath, |
|
|
tile: detail.tile.enabled, |
|
|
offset_x: detail.transform.tx, |
|
|
offset_y: detail.transform.ty, |
|
|
scale: detail.transform.scale, |
|
|
}, |
|
|
}; |
|
|
const data = await postJSON("/api/preview", payload); |
|
|
const urls = |
|
|
data.previews || |
|
|
(data.preview_url ? { [active]: data.preview_url } : null); |
|
|
if (urls) { |
|
|
set((state) => { |
|
|
const updatedDetails = { ...state.details }; |
|
|
for (const [key, url] of Object.entries(urls)) { |
|
|
if (!updatedDetails[key]) continue; |
|
|
updatedDetails[key] = { |
|
|
...updatedDetails[key], |
|
|
previewUrl: url, |
|
|
}; |
|
|
} |
|
|
return { details: updatedDetails, lastPreviewUrls: urls }; |
|
|
}); |
|
|
} else { |
|
|
set({ lastPreviewUrls: urls }); |
|
|
} |
|
|
return data; |
|
|
} finally { |
|
|
set({ busy: false }); |
|
|
} |
|
|
}, |
|
|
|
|
|
|
|
|
async uploadAndSpread(file) { |
|
|
await get().upload(file); |
|
|
return get().spread(); |
|
|
}, |
|
|
|
|
|
|
|
|
async startOrder() { |
|
|
const { model, active, details, uploadedPath } = get(); |
|
|
const detail = details[active]; |
|
|
if (!uploadedPath) throw new Error("Сначала загрузите принт"); |
|
|
|
|
|
const detail = details[active]; |
|
|
set({ busy: true }); |
|
|
try { |
|
|
const payload = { |
|
|
model, |
|
|
view: active, |
|
|
details: { |
|
|
print_path: uploadedPath, |
|
|
tile: detail.tile.enabled, |
|
|
offset_x: detail.transform.tx, |
|
|
offset_y: detail.transform.ty, |
|
|
scale: detail.transform.scale, |
|
|
}, |
|
|
}; |
|
|
const data = await postJSON("/api/order", payload); |
|
|
set({ lastOrderInfo: data }); |
|
|
return data; |
|
|
} finally { |
|
|
set({ busy: false }); |
|
|
} |
|
|
}, |
|
|
})); |
|
|
|
|
|
export default useStore; |
|
|
|