@pisell/private-materials 6.3.220 → 6.3.222
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/lowcode/assets-daily.json +11 -11
- package/build/lowcode/assets-dev.json +2 -2
- package/build/lowcode/assets-prod.json +11 -11
- package/build/lowcode/meta.js +1 -1
- package/build/lowcode/render/default/view.css +1 -1
- package/build/lowcode/render/default/view.js +13 -13
- package/build/lowcode/view.css +1 -1
- package/build/lowcode/view.js +13 -13
- package/es/components/kioskSkuDetail/index.js +7 -7
- package/es/components/pay/toC/PaymentMethods/MWCreditCard/tds2.js +53 -26
- package/es/components/scanOrder/components/Content/index.js +14 -2
- package/es/components/scanOrder/components/ExitConfirmModal/index.js +84 -0
- package/es/components/scanOrder/components/ExitConfirmModal/index.less +182 -0
- package/es/components/scanOrder/locales.js +20 -0
- package/es/index.d.ts +9 -1
- package/es/index.js +6 -2
- package/es/plus/inventory/BindingModal.js +94 -0
- package/es/plus/inventory/InventoryItemManagement.d.ts +7 -0
- package/es/plus/inventory/InventoryItemManagement.js +204 -0
- package/es/plus/inventory/InventoryLocationManagement.d.ts +7 -0
- package/es/plus/inventory/InventoryLocationManagement.js +296 -0
- package/es/plus/inventory/InventoryUnitManagement.d.ts +7 -0
- package/es/plus/inventory/InventoryUnitManagement.js +144 -0
- package/es/plus/inventory/ItemDrawer.js +266 -0
- package/es/plus/inventory/RecordDialog.js +141 -0
- package/es/plus/inventory/StockList.js +132 -0
- package/es/plus/inventory/api.js +66 -0
- package/es/plus/inventory/components.js +172 -0
- package/es/plus/inventory/context.js +118 -0
- package/es/plus/inventory/forms.js +46 -0
- package/es/plus/inventory/hooks.js +235 -0
- package/es/plus/inventory/index.less +559 -0
- package/es/plus/inventory/locales.js +277 -0
- package/es/plus/inventory/types.d.ts +10 -0
- package/es/plus/inventory/utils.js +24 -0
- package/es/plus/panpacificTask/JobCard.js +22 -0
- package/es/plus/panpacificTask/JobDrawer.js +366 -0
- package/es/plus/panpacificTask/JobForm.js +312 -0
- package/es/plus/panpacificTask/JobStatus.js +29 -0
- package/es/plus/panpacificTask/PanpacificTask.d.ts +7 -0
- package/es/plus/panpacificTask/PanpacificTask.js +552 -0
- package/es/plus/panpacificTask/PhotoPreview.js +37 -0
- package/es/plus/panpacificTask/README.md +114 -0
- package/es/plus/panpacificTask/attachments.js +64 -0
- package/es/plus/panpacificTask/index.d.ts +2 -0
- package/es/plus/panpacificTask/index.less +539 -0
- package/es/plus/panpacificTask/locales.js +586 -0
- package/es/plus/panpacificTask/model.js +260 -0
- package/es/plus/panpacificTask/saveJob.js +68 -0
- package/es/plus/panpacificTask/serve.js +243 -0
- package/es/plus/panpacificTask/tenant.js +16 -0
- package/es/plus/panpacificTask/types.d.ts +19 -0
- package/es/plus/pisellReservation/data/bookingToReservationTables.js +1 -1
- package/es/plus/pisellReservation/floorMap/floorPlanApi.js +1 -1
- package/es/plus/pisellReservation/floorMap/reservationMock.js +1 -1
- package/es/plus/pisellSupersetDashboard/README.md +291 -0
- package/es/plus/pisellSupersetDashboard/index.d.ts +15 -0
- package/es/plus/pisellSupersetDashboard/index.js +187 -0
- package/es/plus/pisellSupersetDashboard/index.less +83 -0
- package/es/plus/pisellSupersetDashboard/locales.js +36 -0
- package/es/plus/pisellSupersetDashboard/serve.js +51 -0
- package/es/plus/pisellSupersetDashboard/types.d.ts +17 -0
- package/es/plus/skuOptionsSelection/store/createStore.js +2 -2
- package/es/private-materials/lowcode/authmodal/meta.d.ts +1 -0
- package/es/pro/Login2.0/AuthModal.d.ts +1 -1
- package/es/pro/Login2.0/index.d.ts +1 -2
- package/es/pro/Login2.0/shared/types.d.ts +1 -1
- package/lib/components/kioskSkuDetail/index.js +7 -7
- package/lib/components/pay/toC/PaymentMethods/MWCreditCard/tds2.js +53 -26
- package/lib/components/scanOrder/components/Content/index.js +14 -2
- package/lib/components/scanOrder/components/ExitConfirmModal/index.js +86 -0
- package/lib/components/scanOrder/components/ExitConfirmModal/index.less +182 -0
- package/lib/components/scanOrder/locales.js +20 -0
- package/lib/index.d.ts +9 -1
- package/lib/index.js +136 -126
- package/lib/plus/inventory/BindingModal.js +96 -0
- package/lib/plus/inventory/InventoryItemManagement.d.ts +7 -0
- package/lib/plus/inventory/InventoryItemManagement.js +206 -0
- package/lib/plus/inventory/InventoryLocationManagement.d.ts +7 -0
- package/lib/plus/inventory/InventoryLocationManagement.js +298 -0
- package/lib/plus/inventory/InventoryUnitManagement.d.ts +7 -0
- package/lib/plus/inventory/InventoryUnitManagement.js +146 -0
- package/lib/plus/inventory/ItemDrawer.js +268 -0
- package/lib/plus/inventory/RecordDialog.js +143 -0
- package/lib/plus/inventory/StockList.js +134 -0
- package/lib/plus/inventory/api.js +66 -0
- package/lib/plus/inventory/components.js +181 -0
- package/lib/plus/inventory/context.js +121 -0
- package/lib/plus/inventory/forms.js +50 -0
- package/lib/plus/inventory/hooks.js +240 -0
- package/lib/plus/inventory/index.less +559 -0
- package/lib/plus/inventory/locales.js +277 -0
- package/lib/plus/inventory/types.d.ts +10 -0
- package/lib/plus/inventory/utils.js +30 -0
- package/lib/plus/panpacificTask/JobCard.js +24 -0
- package/lib/plus/panpacificTask/JobDrawer.js +368 -0
- package/lib/plus/panpacificTask/JobForm.js +314 -0
- package/lib/plus/panpacificTask/JobStatus.js +33 -0
- package/lib/plus/panpacificTask/PanpacificTask.d.ts +7 -0
- package/lib/plus/panpacificTask/PanpacificTask.js +555 -0
- package/lib/plus/panpacificTask/PhotoPreview.js +39 -0
- package/lib/plus/panpacificTask/README.md +114 -0
- package/lib/plus/panpacificTask/attachments.js +66 -0
- package/lib/plus/panpacificTask/index.d.ts +2 -0
- package/lib/plus/panpacificTask/index.less +539 -0
- package/lib/plus/panpacificTask/locales.js +586 -0
- package/lib/plus/panpacificTask/model.js +289 -0
- package/lib/plus/panpacificTask/saveJob.js +68 -0
- package/lib/plus/panpacificTask/serve.js +260 -0
- package/lib/plus/panpacificTask/tenant.js +17 -0
- package/lib/plus/panpacificTask/types.d.ts +19 -0
- package/lib/plus/pisellSupersetDashboard/README.md +291 -0
- package/lib/plus/pisellSupersetDashboard/index.d.ts +15 -0
- package/lib/plus/pisellSupersetDashboard/index.js +189 -0
- package/lib/plus/pisellSupersetDashboard/index.less +83 -0
- package/lib/plus/pisellSupersetDashboard/locales.js +36 -0
- package/lib/plus/pisellSupersetDashboard/serve.js +52 -0
- package/lib/plus/pisellSupersetDashboard/types.d.ts +17 -0
- package/lowcode/inventory-item-management/meta.ts +20 -0
- package/lowcode/inventory-location-management/meta.ts +20 -0
- package/lowcode/inventory-unit-management/meta.ts +20 -0
- package/lowcode/panpacific-task/meta.ts +51 -0
- package/lowcode/pisell-superset-dashboard/meta.ts +75 -0
- package/package.json +4 -2
- package/es/plus/pisellReservation/index.js +0 -17
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.js");
|
|
2
|
+
const require_objectSpread2 = require("../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js");
|
|
3
|
+
const require_asyncToGenerator = require("../../_virtual/_@oxc-project_runtime@0.122.0/helpers/asyncToGenerator.js");
|
|
4
|
+
const require_useEngineContext = require("../../hooks/useEngineContext.js");
|
|
5
|
+
const require_index = require("../../utils/index.js");
|
|
6
|
+
const require_attachments = require("./attachments.js");
|
|
7
|
+
const require_model = require("./model.js");
|
|
8
|
+
const require_serve = require("./serve.js");
|
|
9
|
+
const require_JobForm = require("./JobForm.js");
|
|
10
|
+
const require_JobStatus = require("./JobStatus.js");
|
|
11
|
+
const require_JobDrawer = require("./JobDrawer.js");
|
|
12
|
+
const require_JobCard = require("./JobCard.js");
|
|
13
|
+
const require_locales = require("./locales.js");
|
|
14
|
+
let dayjs = require("dayjs");
|
|
15
|
+
dayjs = require_runtime.__toESM(dayjs);
|
|
16
|
+
let react = require("react");
|
|
17
|
+
react = require_runtime.__toESM(react);
|
|
18
|
+
let antd = require("antd");
|
|
19
|
+
let _pisell_utils = require("@pisell/utils");
|
|
20
|
+
let _pisell_materials = require("@pisell/materials");
|
|
21
|
+
require("./index.less");
|
|
22
|
+
//#region src/plus/panpacificTask/PanpacificTask.tsx
|
|
23
|
+
/** Complete #task workspace. All UI state belongs to this component, including its overlays. */
|
|
24
|
+
function PanpacificTask(props) {
|
|
25
|
+
return /* @__PURE__ */ react.default.createElement(antd.ConfigProvider, {
|
|
26
|
+
prefixCls: "pisell-lowcode",
|
|
27
|
+
componentSize: "middle",
|
|
28
|
+
theme: { token: {
|
|
29
|
+
colorPrimary: "#634db3",
|
|
30
|
+
controlHeight: 38,
|
|
31
|
+
controlHeightSM: 38
|
|
32
|
+
} }
|
|
33
|
+
}, /* @__PURE__ */ react.default.createElement(TaskWorkspace, props));
|
|
34
|
+
}
|
|
35
|
+
function TaskWorkspace(props) {
|
|
36
|
+
var _context$appHelper, _context$engine, _context$appHelper2;
|
|
37
|
+
const context = require_useEngineContext.default();
|
|
38
|
+
const utils = (context === null || context === void 0 || (_context$appHelper = context.appHelper) === null || _context$appHelper === void 0 ? void 0 : _context$appHelper.utils) || {};
|
|
39
|
+
if (utils.request) require_index.request.setRequest(utils.request);
|
|
40
|
+
const locale = (context === null || context === void 0 || (_context$engine = context.engine) === null || _context$engine === void 0 || (_context$engine = _context$engine.props) === null || _context$engine === void 0 ? void 0 : _context$engine.locale) || "en";
|
|
41
|
+
_pisell_utils.locales.init(require_locales.default, locale);
|
|
42
|
+
const getText = _pisell_utils.locales.getText;
|
|
43
|
+
const client = (0, react.useMemo)(() => utils.request || require_index.request.getRequest(), [utils.request]);
|
|
44
|
+
const { mediaUploadUrl, getHeaders } = (context === null || context === void 0 || (_context$appHelper2 = context.appHelper) === null || _context$appHelper2 === void 0 ? void 0 : _context$appHelper2.constants) || {};
|
|
45
|
+
const uploadPhoto = (0, react.useMemo)(() => props.uploadPhoto || require_attachments.defaultPhotoUploader({
|
|
46
|
+
mediaUploadUrl,
|
|
47
|
+
getHeaders
|
|
48
|
+
}), [
|
|
49
|
+
props.uploadPhoto,
|
|
50
|
+
mediaUploadUrl,
|
|
51
|
+
getHeaders
|
|
52
|
+
]);
|
|
53
|
+
const [modal, modalContext] = antd.Modal.useModal();
|
|
54
|
+
const [tab, setTab] = (0, react.useState)(props.defaultTab || "calendar");
|
|
55
|
+
const [jobs, setJobs] = (0, react.useState)([]);
|
|
56
|
+
const [base, setBase] = (0, react.useState)(require_model.emptyBase);
|
|
57
|
+
const [loading, setLoading] = (0, react.useState)(false);
|
|
58
|
+
const [busyId, setBusyId] = (0, react.useState)();
|
|
59
|
+
const [month, setMonth] = (0, react.useState)((0, dayjs.default)().format("YYYY-MM"));
|
|
60
|
+
const [selectedDate, setSelectedDate] = (0, react.useState)(require_model.dateText((0, dayjs.default)()));
|
|
61
|
+
const [period, setPeriod] = (0, react.useState)(false);
|
|
62
|
+
const [viewport, setViewport] = (0, react.useState)({
|
|
63
|
+
mode: "month",
|
|
64
|
+
month
|
|
65
|
+
});
|
|
66
|
+
const [ganttStatus, setGanttStatus] = (0, react.useState)();
|
|
67
|
+
const [hoveredJob, setHoveredJob] = (0, react.useState)();
|
|
68
|
+
const [collapsed, setCollapsed] = (0, react.useState)([]);
|
|
69
|
+
const [filters, setFilters] = (0, react.useState)({
|
|
70
|
+
search: "",
|
|
71
|
+
due: ""
|
|
72
|
+
});
|
|
73
|
+
const [page, setPage] = (0, react.useState)(1);
|
|
74
|
+
const [pageSize, setPageSize] = (0, react.useState)(10);
|
|
75
|
+
const [drawer, setDrawer] = (0, react.useState)();
|
|
76
|
+
const [draft, setDraft] = (0, react.useState)();
|
|
77
|
+
const [editorKey, setEditorKey] = (0, react.useState)(0);
|
|
78
|
+
const editorRef = (0, react.useRef)(null);
|
|
79
|
+
const disposed = (0, react.useRef)(false);
|
|
80
|
+
const readSequence = (0, react.useRef)(0);
|
|
81
|
+
const writing = (0, react.useRef)(false);
|
|
82
|
+
const changeCallback = (0, react.useRef)(props.onJobChange);
|
|
83
|
+
changeCallback.current = props.onJobChange;
|
|
84
|
+
const load = (0, react.useCallback)(require_asyncToGenerator._asyncToGenerator(function* () {
|
|
85
|
+
const token = ++readSequence.current;
|
|
86
|
+
setLoading(true);
|
|
87
|
+
try {
|
|
88
|
+
const result = yield require_serve.getJobs(client);
|
|
89
|
+
if (!disposed.current && token === readSequence.current) setJobs(result);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.warn("[PanpacificTask] Job list load failed", error);
|
|
92
|
+
} finally {
|
|
93
|
+
if (!disposed.current && token === readSequence.current) setLoading(false);
|
|
94
|
+
}
|
|
95
|
+
}), [client]);
|
|
96
|
+
(0, react.useEffect)(() => {
|
|
97
|
+
disposed.current = false;
|
|
98
|
+
let cancelled = false;
|
|
99
|
+
load();
|
|
100
|
+
require_serve.getJobBaseData(client).then((result) => {
|
|
101
|
+
if (!cancelled) setBase((previous) => require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, previous), result));
|
|
102
|
+
});
|
|
103
|
+
return () => {
|
|
104
|
+
cancelled = true;
|
|
105
|
+
disposed.current = true;
|
|
106
|
+
readSequence.current++;
|
|
107
|
+
};
|
|
108
|
+
}, [client, load]);
|
|
109
|
+
const confirm = (key) => new Promise((resolve) => modal.confirm({
|
|
110
|
+
title: getText(`pisell2.panpacific.task.${key}`),
|
|
111
|
+
okText: getText(`pisell2.panpacific.task.${key === "discardConfirm" ? "discard" : key === "removeConfirm" ? "remove" : "cancelJob"}`),
|
|
112
|
+
cancelText: getText("pisell2.panpacific.task.cancelAction"),
|
|
113
|
+
okButtonProps: { danger: true },
|
|
114
|
+
onOk: () => resolve(true),
|
|
115
|
+
onCancel: () => resolve(false)
|
|
116
|
+
}));
|
|
117
|
+
const changed = (id) => {
|
|
118
|
+
var _changeCallback$curre;
|
|
119
|
+
load();
|
|
120
|
+
(_changeCallback$curre = changeCallback.current) === null || _changeCallback$curre === void 0 || _changeCallback$curre.call(changeCallback, id);
|
|
121
|
+
};
|
|
122
|
+
const openCreate = (initial = require_model.draftFor(void 0, selectedDate)) => {
|
|
123
|
+
setEditorKey((value) => value + 1);
|
|
124
|
+
setDraft(initial);
|
|
125
|
+
};
|
|
126
|
+
const openJob = (job, edit = false) => {
|
|
127
|
+
if (!writing.current && !draft) setDrawer({
|
|
128
|
+
id: job.id,
|
|
129
|
+
edit
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
const mutate = function() {
|
|
133
|
+
var _ref = require_asyncToGenerator._asyncToGenerator(function* (job, operation, patch) {
|
|
134
|
+
if (writing.current) return false;
|
|
135
|
+
writing.current = true;
|
|
136
|
+
setBusyId(job.id);
|
|
137
|
+
try {
|
|
138
|
+
yield operation();
|
|
139
|
+
if (!disposed.current) {
|
|
140
|
+
var _changeCallback$curre2;
|
|
141
|
+
if (patch) setJobs((previous) => previous.map((row) => String(row.id) === String(job.id) ? require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, row), patch) : row));
|
|
142
|
+
yield load();
|
|
143
|
+
(_changeCallback$curre2 = changeCallback.current) === null || _changeCallback$curre2 === void 0 || _changeCallback$curre2.call(changeCallback, job.id);
|
|
144
|
+
}
|
|
145
|
+
return true;
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (!disposed.current) modal.error({ title: getText(require_serve.errorText(error)) });
|
|
148
|
+
return false;
|
|
149
|
+
} finally {
|
|
150
|
+
writing.current = false;
|
|
151
|
+
if (!disposed.current) setBusyId(void 0);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
return function mutate(_x, _x2, _x3) {
|
|
155
|
+
return _ref.apply(this, arguments);
|
|
156
|
+
};
|
|
157
|
+
}();
|
|
158
|
+
const remove = function() {
|
|
159
|
+
var _ref2 = require_asyncToGenerator._asyncToGenerator(function* (job) {
|
|
160
|
+
if (yield confirm("removeConfirm")) {
|
|
161
|
+
if (yield mutate(job, () => require_serve.removeJob(client, job.id))) setJobs((previous) => previous.filter((row) => String(row.id) !== String(job.id)));
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
return function remove(_x4) {
|
|
165
|
+
return _ref2.apply(this, arguments);
|
|
166
|
+
};
|
|
167
|
+
}();
|
|
168
|
+
const cancel = function() {
|
|
169
|
+
var _ref3 = require_asyncToGenerator._asyncToGenerator(function* (job) {
|
|
170
|
+
if (yield confirm("cancelConfirm")) yield mutate(job, () => require_serve.updateJob(client, job.id, { status: "cancel" }), { status: "cancel" });
|
|
171
|
+
});
|
|
172
|
+
return function cancel(_x5) {
|
|
173
|
+
return _ref3.apply(this, arguments);
|
|
174
|
+
};
|
|
175
|
+
}();
|
|
176
|
+
const requestDates = function() {
|
|
177
|
+
var _ref4 = require_asyncToGenerator._asyncToGenerator(function* (change) {
|
|
178
|
+
const job = jobs.find((row) => String(row.id) === change.item.id);
|
|
179
|
+
if (!job || writing.current) return {
|
|
180
|
+
accepted: false,
|
|
181
|
+
reason: getText("pisell2.panpacific.task.requestFailed")
|
|
182
|
+
};
|
|
183
|
+
const currentRange = require_model.jobRange(job);
|
|
184
|
+
if (!currentRange || currentRange.start !== change.before.start || currentRange.endExclusive !== change.before.endExclusive) return {
|
|
185
|
+
accepted: false,
|
|
186
|
+
reason: getText("pisell2.panpacific.task.requestFailed")
|
|
187
|
+
};
|
|
188
|
+
const patch = require_model.shiftDates(job, change.after, change.action);
|
|
189
|
+
const accepted = yield mutate(job, () => require_serve.updateJob(client, job.id, patch), patch);
|
|
190
|
+
return require_objectSpread2._objectSpread2({ accepted }, accepted ? {} : { reason: getText("pisell2.panpacific.task.requestFailed") });
|
|
191
|
+
});
|
|
192
|
+
return function requestDates(_x6) {
|
|
193
|
+
return _ref4.apply(this, arguments);
|
|
194
|
+
};
|
|
195
|
+
}();
|
|
196
|
+
const statusOptions = require_model.displayStatuses.map((value) => ({
|
|
197
|
+
value,
|
|
198
|
+
label: getText(`pisell2.panpacific.task.${value}`)
|
|
199
|
+
}));
|
|
200
|
+
const statusSelect = (value, onChange) => /* @__PURE__ */ react.default.createElement(antd.Select, {
|
|
201
|
+
size: "small",
|
|
202
|
+
allowClear: true,
|
|
203
|
+
showSearch: true,
|
|
204
|
+
optionFilterProp: "label",
|
|
205
|
+
className: "pp-task-filter-status",
|
|
206
|
+
"aria-label": getText("pisell2.panpacific.task.status"),
|
|
207
|
+
placeholder: getText("pisell2.panpacific.task.allStatuses"),
|
|
208
|
+
options: [{
|
|
209
|
+
value: "",
|
|
210
|
+
label: getText("pisell2.panpacific.task.allStatuses")
|
|
211
|
+
}, ...statusOptions],
|
|
212
|
+
value: value || "",
|
|
213
|
+
onChange: (next) => onChange(next || void 0)
|
|
214
|
+
});
|
|
215
|
+
const deferredFilters = (0, react.useDeferredValue)(filters);
|
|
216
|
+
const filtered = (0, react.useMemo)(() => require_model.filterJobs(jobs, deferredFilters, base), [
|
|
217
|
+
jobs,
|
|
218
|
+
deferredFilters,
|
|
219
|
+
base
|
|
220
|
+
]);
|
|
221
|
+
(0, react.useEffect)(() => {
|
|
222
|
+
const last = Math.max(1, Math.ceil(filtered.length / pageSize));
|
|
223
|
+
if (page > last) setPage(last);
|
|
224
|
+
}, [
|
|
225
|
+
filtered.length,
|
|
226
|
+
page,
|
|
227
|
+
pageSize
|
|
228
|
+
]);
|
|
229
|
+
const updateFilter = (patch) => {
|
|
230
|
+
setFilters((previous) => Object.keys(patch).every((key) => previous[key] === patch[key]) ? previous : require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, previous), patch));
|
|
231
|
+
setPage(1);
|
|
232
|
+
};
|
|
233
|
+
const columns = [
|
|
234
|
+
{
|
|
235
|
+
key: "code",
|
|
236
|
+
title: getText("pisell2.panpacific.task.code"),
|
|
237
|
+
width: 230,
|
|
238
|
+
render: (_, job) => /* @__PURE__ */ react.default.createElement(antd.Button, {
|
|
239
|
+
type: "link",
|
|
240
|
+
className: "pp-task-code-link",
|
|
241
|
+
onClick: () => openJob(job)
|
|
242
|
+
}, job.job_number)
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
key: "supplier",
|
|
246
|
+
title: getText("pisell2.panpacific.task.supplier"),
|
|
247
|
+
width: 210,
|
|
248
|
+
render: (_, job) => require_model.companyName(job) || "—"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
key: "schedule",
|
|
252
|
+
title: getText("pisell2.panpacific.task.schedule"),
|
|
253
|
+
width: 310,
|
|
254
|
+
render: (_, job) => `${require_model.scheduleText(job)} · ${getText("pisell2.panpacific.task.due")} ${require_model.dateText(job.due_date)}`
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
key: "due",
|
|
258
|
+
title: getText("pisell2.panpacific.task.due"),
|
|
259
|
+
width: 130,
|
|
260
|
+
render: (_, job) => require_model.dateText(job.due_date) || "—"
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
key: "status",
|
|
264
|
+
title: getText("pisell2.panpacific.task.status"),
|
|
265
|
+
width: 140,
|
|
266
|
+
render: (_, job) => /* @__PURE__ */ react.default.createElement(require_JobStatus.JobStatus, {
|
|
267
|
+
job,
|
|
268
|
+
getText
|
|
269
|
+
})
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
key: "items",
|
|
273
|
+
title: getText("pisell2.panpacific.task.items"),
|
|
274
|
+
width: 90,
|
|
275
|
+
render: (_, job) => {
|
|
276
|
+
var _job$job_materials$fi, _job$job_materials;
|
|
277
|
+
return (_job$job_materials$fi = (_job$job_materials = job.job_materials) === null || _job$job_materials === void 0 ? void 0 : _job$job_materials.filter((row) => row.deleted_at == null).length) !== null && _job$job_materials$fi !== void 0 ? _job$job_materials$fi : "—";
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
key: "payment",
|
|
282
|
+
title: getText("pisell2.panpacific.task.payment"),
|
|
283
|
+
width: 170,
|
|
284
|
+
render: (_, job) => [require_model.paymentTerm(job, base), job.payment_term_days != null ? `${job.payment_term_days} ${getText("pisell2.panpacific.task.dayUnit")}` : ""].filter(Boolean).join(" · ") || "—"
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
key: "actions",
|
|
288
|
+
fixed: "right",
|
|
289
|
+
title: getText("pisell2.panpacific.task.actions"),
|
|
290
|
+
width: 260,
|
|
291
|
+
render: (_, job) => /* @__PURE__ */ react.default.createElement("div", {
|
|
292
|
+
className: "pp-task-row-actions",
|
|
293
|
+
onClick: (event) => event.stopPropagation()
|
|
294
|
+
}, /* @__PURE__ */ react.default.createElement(antd.Button, {
|
|
295
|
+
size: "small",
|
|
296
|
+
onClick: () => openJob(job)
|
|
297
|
+
}, getText("pisell2.panpacific.task.view")), /* @__PURE__ */ react.default.createElement(antd.Button, {
|
|
298
|
+
size: "small",
|
|
299
|
+
onClick: () => openJob(job, true)
|
|
300
|
+
}, getText("pisell2.panpacific.task.edit")), /* @__PURE__ */ react.default.createElement(antd.Button, {
|
|
301
|
+
size: "small",
|
|
302
|
+
disabled: job.status === "cancel" || busyId != null,
|
|
303
|
+
onClick: () => cancel(job)
|
|
304
|
+
}, getText("pisell2.panpacific.task.cancelJob")), /* @__PURE__ */ react.default.createElement(antd.Button, {
|
|
305
|
+
size: "small",
|
|
306
|
+
danger: true,
|
|
307
|
+
disabled: busyId != null,
|
|
308
|
+
onClick: () => remove(job)
|
|
309
|
+
}, getText("pisell2.panpacific.task.remove")))
|
|
310
|
+
}
|
|
311
|
+
];
|
|
312
|
+
const selectedJobs = (0, react.useMemo)(() => require_model.jobsOnDate(jobs, selectedDate, period), [
|
|
313
|
+
jobs,
|
|
314
|
+
selectedDate,
|
|
315
|
+
period
|
|
316
|
+
]);
|
|
317
|
+
const items = (0, react.useMemo)(() => require_model.calendarItems(jobs), [jobs]);
|
|
318
|
+
const ganttStart = viewport.mode === "month" ? viewport.month + "-01" : viewport.range.start;
|
|
319
|
+
const ganttEnd = viewport.mode === "month" ? (0, dayjs.default)(ganttStart).add(1, "month").format("YYYY-MM-DD") : viewport.range.endExclusive;
|
|
320
|
+
const ganttItems = (0, react.useMemo)(() => require_model.calendarItems(require_model.filterJobs(jobs, { status: ganttStatus }, base)).filter((item) => item.range.start < ganttEnd && item.range.endExclusive > ganttStart), [
|
|
321
|
+
jobs,
|
|
322
|
+
ganttStatus,
|
|
323
|
+
base,
|
|
324
|
+
ganttStart,
|
|
325
|
+
ganttEnd
|
|
326
|
+
]);
|
|
327
|
+
const calendar = /* @__PURE__ */ react.default.createElement(react.default.Fragment, null, /* @__PURE__ */ react.default.createElement(require_JobStatus.StatusLegend, { getText }), /* @__PURE__ */ react.default.createElement("div", { className: "pp-task-calendar-layout" }, /* @__PURE__ */ react.default.createElement("div", { className: "pp-task-calendar-main" }, /* @__PURE__ */ react.default.createElement(_pisell_materials.PisellCalendar, {
|
|
328
|
+
items,
|
|
329
|
+
today: require_model.dateText((0, dayjs.default)()),
|
|
330
|
+
month,
|
|
331
|
+
onMonthChange: setMonth,
|
|
332
|
+
selectedDate,
|
|
333
|
+
onSelectedDateChange: setSelectedDate,
|
|
334
|
+
displayMode: period ? "range" : "custom-date",
|
|
335
|
+
getDisplayDate: (item) => {
|
|
336
|
+
var _item$data;
|
|
337
|
+
return require_model.dateText((_item$data = item.data) === null || _item$data === void 0 ? void 0 : _item$data.due_date);
|
|
338
|
+
},
|
|
339
|
+
loading,
|
|
340
|
+
interaction: { move: true },
|
|
341
|
+
moveAnchor: "grabbed-date",
|
|
342
|
+
canStartInteraction: () => ({ allowed: !writing.current }),
|
|
343
|
+
onRequestChange: requestDates,
|
|
344
|
+
onChangeCommitted: (change) => setSelectedDate(change.target.date),
|
|
345
|
+
onItemClick: ({ item }) => item.data && openJob(item.data),
|
|
346
|
+
onDateDoubleClick: ({ date }) => {
|
|
347
|
+
setSelectedDate(date);
|
|
348
|
+
openCreate(require_model.draftFor(void 0, date));
|
|
349
|
+
},
|
|
350
|
+
selectedItemId: drawer && String(drawer.id),
|
|
351
|
+
density: "compact",
|
|
352
|
+
maxEventsPerDay: 4,
|
|
353
|
+
classNames: { overflowPanel: "pp-task-calendar-popup" },
|
|
354
|
+
locale,
|
|
355
|
+
toolbarExtra: /* @__PURE__ */ react.default.createElement("label", { className: "pp-task-period" }, /* @__PURE__ */ react.default.createElement(antd.Switch, {
|
|
356
|
+
size: "small",
|
|
357
|
+
checked: period,
|
|
358
|
+
onChange: setPeriod
|
|
359
|
+
}), getText("pisell2.panpacific.task.period")),
|
|
360
|
+
styles: { event: (ctx) => require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, require_model.jobStyle((ctx === null || ctx === void 0 ? void 0 : ctx.item.data) || {})), (ctx === null || ctx === void 0 ? void 0 : ctx.item.id) === hoveredJob ? {
|
|
361
|
+
outline: "2px solid #634db3",
|
|
362
|
+
outlineOffset: "-2px"
|
|
363
|
+
} : {}) },
|
|
364
|
+
renderEventContent: ({ item }) => /* @__PURE__ */ react.default.createElement("span", { className: "pp-task-event-content" }, /* @__PURE__ */ react.default.createElement("span", { title: item.title }, item.title), require_model.hasInvoice(item.data || {}) && /* @__PURE__ */ react.default.createElement(require_JobStatus.InvoiceMark, { getText }))
|
|
365
|
+
})), /* @__PURE__ */ react.default.createElement("aside", { className: "pp-task-calendar-side" }, /* @__PURE__ */ react.default.createElement("h2", null, selectedDate), /* @__PURE__ */ react.default.createElement("p", null, selectedJobs.length, " ", getText("pisell2.panpacific.task.job")), /* @__PURE__ */ react.default.createElement(antd.Button, {
|
|
366
|
+
block: true,
|
|
367
|
+
type: "primary",
|
|
368
|
+
onClick: () => openCreate()
|
|
369
|
+
}, "+ ", getText("pisell2.panpacific.task.createJob")), /* @__PURE__ */ react.default.createElement("div", {
|
|
370
|
+
className: "pp-task-job-cards pp-job-list",
|
|
371
|
+
role: "list"
|
|
372
|
+
}, selectedJobs.length ? selectedJobs.map((job) => /* @__PURE__ */ react.default.createElement("div", {
|
|
373
|
+
role: "listitem",
|
|
374
|
+
key: String(job.id)
|
|
375
|
+
}, /* @__PURE__ */ react.default.createElement(require_JobCard.JobCard, {
|
|
376
|
+
job,
|
|
377
|
+
getText,
|
|
378
|
+
selected: String(drawer === null || drawer === void 0 ? void 0 : drawer.id) === String(job.id),
|
|
379
|
+
onOpen: () => openJob(job),
|
|
380
|
+
onHover: setHoveredJob
|
|
381
|
+
}))) : /* @__PURE__ */ react.default.createElement(antd.Empty, {
|
|
382
|
+
image: antd.Empty.PRESENTED_IMAGE_SIMPLE,
|
|
383
|
+
description: getText("pisell2.panpacific.task.noJobs")
|
|
384
|
+
})))));
|
|
385
|
+
const timeline = /* @__PURE__ */ react.default.createElement(react.default.Fragment, null, /* @__PURE__ */ react.default.createElement(require_JobStatus.StatusLegend, { getText }), /* @__PURE__ */ react.default.createElement(_pisell_materials.PisellGantt, {
|
|
386
|
+
items: ganttItems,
|
|
387
|
+
today: require_model.dateText((0, dayjs.default)()),
|
|
388
|
+
viewport,
|
|
389
|
+
onViewportChange: setViewport,
|
|
390
|
+
loading,
|
|
391
|
+
locale,
|
|
392
|
+
rowHeight: 60,
|
|
393
|
+
getGroupKey: (item) => {
|
|
394
|
+
var _item$data2;
|
|
395
|
+
return ((_item$data2 = item.data) === null || _item$data2 === void 0 ? void 0 : _item$data2.status) || "create";
|
|
396
|
+
},
|
|
397
|
+
groups: require_model.statuses.filter((id) => ganttItems.some((item) => item.data.status === id)).map((id) => ({
|
|
398
|
+
id,
|
|
399
|
+
title: getText(`pisell2.panpacific.task.${id}`)
|
|
400
|
+
})),
|
|
401
|
+
collapsedGroupIds: collapsed,
|
|
402
|
+
onCollapsedGroupIdsChange: setCollapsed,
|
|
403
|
+
toolbarExtra: /* @__PURE__ */ react.default.createElement("label", { className: "pp-task-timeline-filter" }, /* @__PURE__ */ react.default.createElement("span", null, getText("pisell2.panpacific.task.status")), statusSelect(ganttStatus, setGanttStatus)),
|
|
404
|
+
selectedItemId: drawer && String(drawer.id),
|
|
405
|
+
onItemClick: ({ item }) => item.data && openJob(item.data),
|
|
406
|
+
interaction: {
|
|
407
|
+
move: true,
|
|
408
|
+
resizeStart: true,
|
|
409
|
+
resizeEnd: true
|
|
410
|
+
},
|
|
411
|
+
canStartInteraction: () => ({ allowed: !writing.current }),
|
|
412
|
+
onRequestChange: requestDates,
|
|
413
|
+
renderRowLabel: ({ item }) => /* @__PURE__ */ react.default.createElement(antd.Button, {
|
|
414
|
+
type: "text",
|
|
415
|
+
onClick: () => item.data && openJob(item.data)
|
|
416
|
+
}, item.title),
|
|
417
|
+
styles: { taskBar: (ctx) => require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, require_model.jobStyle((ctx === null || ctx === void 0 ? void 0 : ctx.item.data) || {})), {}, { border: "1px solid #d1d5db" }) },
|
|
418
|
+
renderTaskContent: ({ item }) => {
|
|
419
|
+
var _item$data3, _item$data4;
|
|
420
|
+
return /* @__PURE__ */ react.default.createElement("div", { className: "pp-task-gantt-content" }, /* @__PURE__ */ react.default.createElement("strong", null, item.title), /* @__PURE__ */ react.default.createElement("small", null, ((_item$data3 = item.data) === null || _item$data3 === void 0 ? void 0 : _item$data3.company_contact) || "—", " · ", require_model.dateText((_item$data4 = item.data) === null || _item$data4 === void 0 ? void 0 : _item$data4.due_date), require_model.hasInvoice(item.data || {}) && /* @__PURE__ */ react.default.createElement(require_JobStatus.InvoiceMark, { getText })));
|
|
421
|
+
}
|
|
422
|
+
}));
|
|
423
|
+
const list = /* @__PURE__ */ react.default.createElement(react.default.Fragment, null, /* @__PURE__ */ react.default.createElement("div", { className: "pp-task-filters" }, /* @__PURE__ */ react.default.createElement("label", null, /* @__PURE__ */ react.default.createElement("span", null, getText("pisell2.panpacific.task.search")), /* @__PURE__ */ react.default.createElement(antd.Input, {
|
|
424
|
+
allowClear: true,
|
|
425
|
+
placeholder: getText("pisell2.panpacific.task.searchHint"),
|
|
426
|
+
value: filters.search,
|
|
427
|
+
"aria-label": getText("pisell2.panpacific.task.search"),
|
|
428
|
+
onInput: (event) => updateFilter({ search: event.currentTarget.value }),
|
|
429
|
+
onChange: (event) => updateFilter({ search: event.target.value })
|
|
430
|
+
})), /* @__PURE__ */ react.default.createElement("label", null, /* @__PURE__ */ react.default.createElement("span", null, getText("pisell2.panpacific.task.due")), /* @__PURE__ */ react.default.createElement(antd.DatePicker, {
|
|
431
|
+
allowClear: true,
|
|
432
|
+
placeholder: getText("pisell2.panpacific.task.allDates"),
|
|
433
|
+
value: filters.due ? (0, dayjs.default)(filters.due) : null,
|
|
434
|
+
onChange: (value) => updateFilter({ due: require_model.dateText(value) })
|
|
435
|
+
})), /* @__PURE__ */ react.default.createElement("label", null, /* @__PURE__ */ react.default.createElement("span", null, getText("pisell2.panpacific.task.status")), statusSelect(filters.status, (status) => updateFilter({ status }))), /* @__PURE__ */ react.default.createElement(antd.Button, { onClick: () => {
|
|
436
|
+
setFilters({
|
|
437
|
+
search: "",
|
|
438
|
+
due: ""
|
|
439
|
+
});
|
|
440
|
+
setPage(1);
|
|
441
|
+
} }, getText("pisell2.panpacific.task.reset")), /* @__PURE__ */ react.default.createElement(antd.Button, {
|
|
442
|
+
type: "primary",
|
|
443
|
+
onClick: () => openCreate()
|
|
444
|
+
}, "+ ", getText("pisell2.panpacific.task.createJob"))), /* @__PURE__ */ react.default.createElement(antd.Table, {
|
|
445
|
+
size: "small",
|
|
446
|
+
rowKey: (job) => String(job.id),
|
|
447
|
+
columns,
|
|
448
|
+
dataSource: filtered,
|
|
449
|
+
loading,
|
|
450
|
+
scroll: { x: 1640 },
|
|
451
|
+
locale: { emptyText: getText("pisell2.panpacific.task.noJobs") },
|
|
452
|
+
pagination: {
|
|
453
|
+
current: page,
|
|
454
|
+
pageSize,
|
|
455
|
+
showSizeChanger: true,
|
|
456
|
+
onChange: (next, size) => {
|
|
457
|
+
setPage(size !== pageSize ? 1 : next);
|
|
458
|
+
setPageSize(size);
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
onRow: (job) => ({ onClick: (event) => {
|
|
462
|
+
if (!event.target.closest("button, a, input")) openJob(job);
|
|
463
|
+
} })
|
|
464
|
+
}));
|
|
465
|
+
return /* @__PURE__ */ react.default.createElement("div", {
|
|
466
|
+
className: ["pp-task", props.className].filter(Boolean).join(" "),
|
|
467
|
+
style: props.style
|
|
468
|
+
}, modalContext, /* @__PURE__ */ react.default.createElement("header", { className: "pp-task-heading" }, /* @__PURE__ */ react.default.createElement("h1", null, getText("pisell2.panpacific.task.job"))), /* @__PURE__ */ react.default.createElement(antd.Tabs, {
|
|
469
|
+
activeKey: tab,
|
|
470
|
+
onChange: (value) => setTab(value),
|
|
471
|
+
destroyInactiveTabPane: true,
|
|
472
|
+
items: [
|
|
473
|
+
{
|
|
474
|
+
key: "calendar",
|
|
475
|
+
label: getText("pisell2.panpacific.task.calendar"),
|
|
476
|
+
children: calendar
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
key: "timeline",
|
|
480
|
+
label: getText("pisell2.panpacific.task.timeline"),
|
|
481
|
+
children: timeline
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
key: "list",
|
|
485
|
+
label: getText("pisell2.panpacific.task.list"),
|
|
486
|
+
children: list
|
|
487
|
+
}
|
|
488
|
+
]
|
|
489
|
+
}), /* @__PURE__ */ react.default.createElement(antd.Modal, {
|
|
490
|
+
open: !!draft,
|
|
491
|
+
title: getText("pisell2.panpacific.task.createJob"),
|
|
492
|
+
width: "min(1100px, 96vw)",
|
|
493
|
+
wrapClassName: "pp-task-overlay pp-task-modal",
|
|
494
|
+
footer: null,
|
|
495
|
+
destroyOnClose: true,
|
|
496
|
+
maskClosable: false,
|
|
497
|
+
onCancel: () => {
|
|
498
|
+
var _editorRef$current;
|
|
499
|
+
return (_editorRef$current = editorRef.current) === null || _editorRef$current === void 0 ? void 0 : _editorRef$current.close();
|
|
500
|
+
},
|
|
501
|
+
bodyStyle: {
|
|
502
|
+
maxHeight: "76vh",
|
|
503
|
+
overflowY: "auto",
|
|
504
|
+
position: "relative"
|
|
505
|
+
}
|
|
506
|
+
}, draft && /* @__PURE__ */ react.default.createElement(require_JobForm.JobForm, {
|
|
507
|
+
key: editorKey,
|
|
508
|
+
ref: editorRef,
|
|
509
|
+
initial: draft,
|
|
510
|
+
jobs,
|
|
511
|
+
base,
|
|
512
|
+
request: client,
|
|
513
|
+
getText,
|
|
514
|
+
confirmDiscard: () => confirm("discardConfirm"),
|
|
515
|
+
onCancel: () => {
|
|
516
|
+
setDraft(void 0);
|
|
517
|
+
load();
|
|
518
|
+
},
|
|
519
|
+
onSaved: (id, values) => {
|
|
520
|
+
var _jobRange;
|
|
521
|
+
const date = period ? (_jobRange = require_model.jobRange(values)) === null || _jobRange === void 0 ? void 0 : _jobRange.start : require_model.dateText(values.due_date);
|
|
522
|
+
if (date) {
|
|
523
|
+
setSelectedDate(date);
|
|
524
|
+
setMonth(date.slice(0, 7));
|
|
525
|
+
setViewport({
|
|
526
|
+
mode: "month",
|
|
527
|
+
month: date.slice(0, 7)
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
setDraft(void 0);
|
|
531
|
+
changed(id);
|
|
532
|
+
}
|
|
533
|
+
})), drawer && /* @__PURE__ */ react.default.createElement(require_JobDrawer.JobDrawer, {
|
|
534
|
+
key: `${drawer.id}:${!!drawer.edit}`,
|
|
535
|
+
id: drawer.id,
|
|
536
|
+
startEditing: drawer.edit,
|
|
537
|
+
jobs,
|
|
538
|
+
base,
|
|
539
|
+
request: client,
|
|
540
|
+
getText,
|
|
541
|
+
uploadPhoto,
|
|
542
|
+
confirm,
|
|
543
|
+
onClose: () => {
|
|
544
|
+
setDrawer(void 0);
|
|
545
|
+
load();
|
|
546
|
+
},
|
|
547
|
+
onChanged: changed,
|
|
548
|
+
onDuplicate: (initial) => {
|
|
549
|
+
setDrawer(void 0);
|
|
550
|
+
openCreate(initial);
|
|
551
|
+
}
|
|
552
|
+
}));
|
|
553
|
+
}
|
|
554
|
+
//#endregion
|
|
555
|
+
exports.default = PanpacificTask;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.js");
|
|
2
|
+
const require_attachments = require("./attachments.js");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
react = require_runtime.__toESM(react);
|
|
5
|
+
let antd = require("antd");
|
|
6
|
+
let _ant_design_icons = require("@ant-design/icons");
|
|
7
|
+
//#region src/plus/panpacificTask/PhotoPreview.tsx
|
|
8
|
+
function PhotoPreview({ value, label, compact, disabled, getText, onRemove }) {
|
|
9
|
+
const [visible, setVisible] = (0, react.useState)(false);
|
|
10
|
+
const images = require_attachments.attachments(value);
|
|
11
|
+
const remove = (index) => {
|
|
12
|
+
if (disabled || !onRemove) return;
|
|
13
|
+
setVisible(false);
|
|
14
|
+
onRemove(index);
|
|
15
|
+
};
|
|
16
|
+
const removeLabel = getText("pisell2.panpacific.task.removePhoto");
|
|
17
|
+
return /* @__PURE__ */ react.default.createElement(antd.Image.PreviewGroup, { preview: {
|
|
18
|
+
visible,
|
|
19
|
+
onVisibleChange: setVisible,
|
|
20
|
+
rootClassName: "pp-task-photo-preview",
|
|
21
|
+
toolbarRender: (toolbar, { current }) => /* @__PURE__ */ react.default.createElement(react.default.Fragment, null, toolbar, onRemove && /* @__PURE__ */ react.default.createElement(antd.Button, {
|
|
22
|
+
className: "pp-task-photo-preview-remove",
|
|
23
|
+
danger: true,
|
|
24
|
+
icon: /* @__PURE__ */ react.default.createElement(_ant_design_icons.DeleteOutlined, null),
|
|
25
|
+
disabled,
|
|
26
|
+
onClick: () => remove(current)
|
|
27
|
+
}, removeLabel))
|
|
28
|
+
} }, images.map((item, index) => /* @__PURE__ */ react.default.createElement("figure", {
|
|
29
|
+
className: "pp-task-photo",
|
|
30
|
+
key: `${item.url}-${index}`
|
|
31
|
+
}, /* @__PURE__ */ react.default.createElement(antd.Image, {
|
|
32
|
+
width: compact ? 38 : 92,
|
|
33
|
+
height: compact ? 38 : 72,
|
|
34
|
+
src: item.url,
|
|
35
|
+
alt: item.name || label || getText("pisell2.panpacific.task.photo")
|
|
36
|
+
}), !compact && /* @__PURE__ */ react.default.createElement("figcaption", null, label || item.name))));
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
exports.PhotoPreview = PhotoPreview;
|