File size: 3,064 Bytes
256b016
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// frontend/src/useStore.js
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 useStore = create((set, get) => ({
  // состояние
  model: "MT",
  view: "front",
  tile: false,
  offsetX: 0,
  offsetY: 0,
  scale: 1,
  uploadedPath: null,
  busy: false,
  lastPreviewUrls: null,
  lastOrderInfo: null,

  // сеттеры
  setModel: (model) => set({ model }),
  setView: (view) => set({ view }),
  setTile: (tile) => set({ tile }),
  setOffsetX: (offsetX) => set({ offsetX }),
  setOffsetY: (offsetY) => set({ offsetY }),
  setScale: (scale) => set({ scale }),

  // 1) загрузка принта
  async upload(file) {
    set({ busy: true });
    try {
      const { path } = await postFile("/api/upload", file);
      set({ uploadedPath: path });
      return path;
    } finally {
      set({ busy: false });
    }
  },

  // 2) построение превью из текущих контролов
  async spread() {
    const { model, view, tile, offsetX, offsetY, scale, uploadedPath } = get();
    if (!uploadedPath) throw new Error("Сначала загрузите принт");

    set({ busy: true });
    try {
      const payload = {
        model,
        view,
        details: {
          print_path: uploadedPath,
          tile,
          offset_x: offsetX,
          offset_y: offsetY,
          scale,
        },
      };
      const data = await postJSON("/api/preview", payload);
      const urls =
        data.previews ||
        (data.preview_url ? { [view]: data.preview_url } : null);
      set({ lastPreviewUrls: urls });
      return data;
    } finally {
      set({ busy: false });
    }
  },

  // удобный комбинированный экшен под кнопку "Upload / Spread"
  async uploadAndSpread(file) {
    await get().upload(file);
    return get().spread();
  },

  // 3) создание заказа
  async startOrder() {
    const { model, view, tile, offsetX, offsetY, scale, uploadedPath } = get();
    if (!uploadedPath) throw new Error("Сначала загрузите принт");

    set({ busy: true });
    try {
      const payload = {
        model,
        view,
        details: {
          print_path: uploadedPath,
          tile,
          offset_x: offsetX,
          offset_y: offsetY,
          scale,
        },
      };
      const data = await postJSON("/api/order", payload);
      set({ lastOrderInfo: data });
      return data;
    } finally {
      set({ busy: false });
    }
  },
}));

export default useStore;