medusajs-documents-plugin 1.1.49 → 1.1.51

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.
@@ -422,6 +422,18 @@ const DocumentsPage = () => {
422
422
  const [customDocModalOpen, setCustomDocModalOpen] = react.useState(false);
423
423
  const [sendModalOpen, setSendModalOpen] = react.useState(false);
424
424
  const [selectedDoc, setSelectedDoc] = react.useState(null);
425
+ const [editModalOpen, setEditModalOpen] = react.useState(false);
426
+ const [editingDoc, setEditingDoc] = react.useState(null);
427
+ const [isUpdatingDoc, setIsUpdatingDoc] = react.useState(false);
428
+ const [editForm, setEditForm] = react.useState({
429
+ title: "",
430
+ customer_name: "",
431
+ customer_email: "",
432
+ customer_phone: "",
433
+ notes: "",
434
+ terms: "",
435
+ items: []
436
+ });
425
437
  const [settings, setSettings] = react.useState({
426
438
  company_name: "Store",
427
439
  storefront_url: "",
@@ -983,6 +995,77 @@ const DocumentsPage = () => {
983
995
  alert("Error sending notification.");
984
996
  }
985
997
  };
998
+ const handleOpenEditModal = (doc) => {
999
+ setEditingDoc(doc);
1000
+ let meta = doc.metadata || {};
1001
+ if (typeof meta === "string") {
1002
+ try {
1003
+ meta = JSON.parse(meta);
1004
+ } catch (_) {
1005
+ meta = {};
1006
+ }
1007
+ }
1008
+ const docItems = Array.isArray(meta == null ? void 0 : meta.items) && meta.items.length > 0 ? meta.items.map((it) => ({
1009
+ title: it.title || it.name || "Item",
1010
+ description: it.description || "",
1011
+ quantity: Number(it.quantity || 1),
1012
+ unit_price: Number(it.unit_price || it.rate || 0)
1013
+ })) : [
1014
+ {
1015
+ title: doc.title || "Service / Goods",
1016
+ description: "",
1017
+ quantity: 1,
1018
+ unit_price: Number(doc.total_amount || 0)
1019
+ }
1020
+ ];
1021
+ setEditForm({
1022
+ title: doc.title || (doc.type === "invoice" ? "Tax Invoice" : "Quotation"),
1023
+ customer_name: doc.customer_name || "",
1024
+ customer_email: doc.customer_email || "",
1025
+ customer_phone: doc.customer_phone || "",
1026
+ notes: (meta == null ? void 0 : meta.notes) || "",
1027
+ terms: (meta == null ? void 0 : meta.terms) || "",
1028
+ items: docItems
1029
+ });
1030
+ setEditModalOpen(true);
1031
+ };
1032
+ const handleSaveEditDocument = async (e) => {
1033
+ if (!editingDoc) return;
1034
+ setIsUpdatingDoc(true);
1035
+ try {
1036
+ const calculatedTotal = editForm.items.reduce(
1037
+ (sum, it) => sum + Number(it.quantity || 1) * Number(it.unit_price || 0),
1038
+ 0
1039
+ );
1040
+ const res = await fetch(`/admin/documents/${editingDoc.id}`, {
1041
+ method: "PUT",
1042
+ headers: { "Content-Type": "application/json" },
1043
+ credentials: "include",
1044
+ body: JSON.stringify({
1045
+ title: editForm.title,
1046
+ customer_name: editForm.customer_name,
1047
+ customer_email: editForm.customer_email,
1048
+ customer_phone: editForm.customer_phone,
1049
+ notes: editForm.notes,
1050
+ terms: editForm.terms,
1051
+ items: editForm.items,
1052
+ total: calculatedTotal
1053
+ })
1054
+ });
1055
+ const data = await res.json();
1056
+ if (!res.ok || data.error) {
1057
+ throw new Error(data.message || "Failed to update document");
1058
+ }
1059
+ setEditModalOpen(false);
1060
+ setEditingDoc(null);
1061
+ await loadDocuments();
1062
+ alert("Document updated successfully and synced with Zoho Books!");
1063
+ } catch (err) {
1064
+ alert(`Error updating document: ${err.message}`);
1065
+ } finally {
1066
+ setIsUpdatingDoc(false);
1067
+ }
1068
+ };
986
1069
  const handleSyncToZoho = async (docId) => {
987
1070
  try {
988
1071
  const res = await fetch(`/admin/documents/${docId}/sync-zoho`, {
@@ -1365,6 +1448,16 @@ const DocumentsPage = () => {
1365
1448
  children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownTray, { className: "w-4 h-4" })
1366
1449
  }
1367
1450
  ),
1451
+ (doc.type === "invoice" || doc.type === "estimate" || doc.type === "quote") && /* @__PURE__ */ jsxRuntime.jsx(
1452
+ "button",
1453
+ {
1454
+ type: "button",
1455
+ onClick: () => handleOpenEditModal(doc),
1456
+ className: "p-1.5 rounded hover:bg-ui-bg-subtle text-ui-fg-subtle hover:text-purple-600 cursor-pointer",
1457
+ title: "Edit Quote / Invoice & Sync with Zoho Books",
1458
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "w-4 h-4" })
1459
+ }
1460
+ ),
1368
1461
  /* @__PURE__ */ jsxRuntime.jsx(
1369
1462
  "button",
1370
1463
  {
@@ -1512,6 +1605,16 @@ const DocumentsPage = () => {
1512
1605
  children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownTray, { className: "w-3.5 h-3.5" })
1513
1606
  }
1514
1607
  ),
1608
+ (subDoc.type === "invoice" || subDoc.type === "estimate" || subDoc.type === "quote") && /* @__PURE__ */ jsxRuntime.jsx(
1609
+ "button",
1610
+ {
1611
+ type: "button",
1612
+ onClick: () => handleOpenEditModal(subDoc),
1613
+ className: "p-1 rounded hover:bg-ui-bg-subtle text-ui-fg-subtle hover:text-purple-600",
1614
+ title: "Edit Quote / Invoice & Sync with Zoho Books",
1615
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "w-3.5 h-3.5" })
1616
+ }
1617
+ ),
1515
1618
  /* @__PURE__ */ jsxRuntime.jsx(
1516
1619
  "button",
1517
1620
  {
@@ -1607,6 +1710,16 @@ const DocumentsPage = () => {
1607
1710
  children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownTray, { className: "w-4 h-4" })
1608
1711
  }
1609
1712
  ),
1713
+ (doc.type === "invoice" || doc.type === "estimate" || doc.type === "quote") && /* @__PURE__ */ jsxRuntime.jsx(
1714
+ "button",
1715
+ {
1716
+ type: "button",
1717
+ onClick: () => handleOpenEditModal(doc),
1718
+ className: "p-1.5 rounded hover:bg-ui-bg-subtle text-ui-fg-subtle hover:text-purple-600 cursor-pointer",
1719
+ title: "Edit Quote / Invoice & Sync with Zoho Books",
1720
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "w-4 h-4" })
1721
+ }
1722
+ ),
1610
1723
  /* @__PURE__ */ jsxRuntime.jsx(
1611
1724
  "button",
1612
1725
  {
@@ -3977,6 +4090,241 @@ Your *{document_title} #{display_id}* has been generated.
3977
4090
  )
3978
4091
  ] }) })
3979
4092
  ] })
4093
+ ] }) }),
4094
+ editModalOpen && editingDoc && /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal, { open: editModalOpen, onOpenChange: setEditModalOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Content, { children: [
4095
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Header, { className: "flex items-center justify-between px-6 py-4 border-b border-ui-border-base", children: [
4096
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
4097
+ /* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "w-5 h-5 text-purple-600" }),
4098
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Heading, { level: "h2", children: [
4099
+ "Edit ",
4100
+ editingDoc.type === "invoice" ? "Invoice" : "Quote / Estimate",
4101
+ " #",
4102
+ editingDoc.display_id
4103
+ ] }),
4104
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "purple", size: "small", children: "Zoho Synced" })
4105
+ ] }),
4106
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
4107
+ /* @__PURE__ */ jsxRuntime.jsx(
4108
+ ui.Button,
4109
+ {
4110
+ variant: "secondary",
4111
+ size: "small",
4112
+ onClick: () => setEditModalOpen(false),
4113
+ disabled: isUpdatingDoc,
4114
+ children: "Cancel"
4115
+ }
4116
+ ),
4117
+ /* @__PURE__ */ jsxRuntime.jsx(
4118
+ ui.Button,
4119
+ {
4120
+ variant: "primary",
4121
+ size: "small",
4122
+ onClick: () => handleSaveEditDocument(),
4123
+ isLoading: isUpdatingDoc,
4124
+ disabled: isUpdatingDoc,
4125
+ className: "bg-purple-600 hover:bg-purple-700 text-white",
4126
+ children: "Save & Sync to Zoho Books"
4127
+ }
4128
+ )
4129
+ ] })
4130
+ ] }),
4131
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Body, { className: "p-6 flex flex-col gap-6 max-w-3xl mx-auto overflow-y-auto max-h-[80vh]", children: [
4132
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
4133
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4134
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Document Title" }),
4135
+ /* @__PURE__ */ jsxRuntime.jsx(
4136
+ ui.Input,
4137
+ {
4138
+ value: editForm.title,
4139
+ onChange: (e) => setEditForm({ ...editForm, title: e.target.value }),
4140
+ size: "small"
4141
+ }
4142
+ )
4143
+ ] }),
4144
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4145
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Customer Name" }),
4146
+ /* @__PURE__ */ jsxRuntime.jsx(
4147
+ ui.Input,
4148
+ {
4149
+ value: editForm.customer_name,
4150
+ onChange: (e) => setEditForm({ ...editForm, customer_name: e.target.value }),
4151
+ size: "small"
4152
+ }
4153
+ )
4154
+ ] }),
4155
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4156
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Customer Email" }),
4157
+ /* @__PURE__ */ jsxRuntime.jsx(
4158
+ ui.Input,
4159
+ {
4160
+ value: editForm.customer_email,
4161
+ onChange: (e) => setEditForm({ ...editForm, customer_email: e.target.value }),
4162
+ size: "small"
4163
+ }
4164
+ )
4165
+ ] }),
4166
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4167
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Customer Phone" }),
4168
+ /* @__PURE__ */ jsxRuntime.jsx(
4169
+ ui.Input,
4170
+ {
4171
+ value: editForm.customer_phone,
4172
+ onChange: (e) => setEditForm({ ...editForm, customer_phone: e.target.value }),
4173
+ size: "small"
4174
+ }
4175
+ )
4176
+ ] })
4177
+ ] }),
4178
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
4179
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
4180
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs font-semibold", children: "Itemized Line Items" }),
4181
+ /* @__PURE__ */ jsxRuntime.jsxs(
4182
+ ui.Button,
4183
+ {
4184
+ variant: "secondary",
4185
+ size: "small",
4186
+ onClick: () => setEditForm({
4187
+ ...editForm,
4188
+ items: [
4189
+ ...editForm.items,
4190
+ { title: "", description: "", quantity: 1, unit_price: 0 }
4191
+ ]
4192
+ }),
4193
+ children: [
4194
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, { className: "w-3.5 h-3.5 mr-1" }),
4195
+ " Add Line Item"
4196
+ ]
4197
+ }
4198
+ )
4199
+ ] }),
4200
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: editForm.items.map((it, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
4201
+ "div",
4202
+ {
4203
+ className: "p-3 border border-ui-border-base rounded-lg bg-ui-bg-base flex flex-col gap-2",
4204
+ children: [
4205
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-12 gap-2 items-start", children: [
4206
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-6 flex flex-col gap-1", children: [
4207
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-[11px] text-ui-fg-subtle", children: "Item Title *" }),
4208
+ /* @__PURE__ */ jsxRuntime.jsx(
4209
+ ui.Input,
4210
+ {
4211
+ placeholder: "Item name / Service",
4212
+ value: it.title,
4213
+ onChange: (e) => {
4214
+ const newItems = [...editForm.items];
4215
+ newItems[idx] = { ...newItems[idx], title: e.target.value };
4216
+ setEditForm({ ...editForm, items: newItems });
4217
+ },
4218
+ size: "small"
4219
+ }
4220
+ )
4221
+ ] }),
4222
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-2 flex flex-col gap-1", children: [
4223
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-[11px] text-ui-fg-subtle", children: "Qty" }),
4224
+ /* @__PURE__ */ jsxRuntime.jsx(
4225
+ ui.Input,
4226
+ {
4227
+ type: "number",
4228
+ min: "1",
4229
+ value: it.quantity,
4230
+ onChange: (e) => {
4231
+ const newItems = [...editForm.items];
4232
+ newItems[idx] = { ...newItems[idx], quantity: Number(e.target.value) || 1 };
4233
+ setEditForm({ ...editForm, items: newItems });
4234
+ },
4235
+ size: "small"
4236
+ }
4237
+ )
4238
+ ] }),
4239
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-3 flex flex-col gap-1", children: [
4240
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-[11px] text-ui-fg-subtle", children: "Unit Price" }),
4241
+ /* @__PURE__ */ jsxRuntime.jsx(
4242
+ ui.Input,
4243
+ {
4244
+ type: "number",
4245
+ step: "0.01",
4246
+ value: it.unit_price,
4247
+ onChange: (e) => {
4248
+ const newItems = [...editForm.items];
4249
+ newItems[idx] = { ...newItems[idx], unit_price: Number(e.target.value) || 0 };
4250
+ setEditForm({ ...editForm, items: newItems });
4251
+ },
4252
+ size: "small"
4253
+ }
4254
+ )
4255
+ ] }),
4256
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-1 pt-6 flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
4257
+ ui.Button,
4258
+ {
4259
+ variant: "transparent",
4260
+ size: "small",
4261
+ onClick: () => {
4262
+ const newItems = editForm.items.filter((_, i) => i !== idx);
4263
+ setEditForm({ ...editForm, items: newItems });
4264
+ },
4265
+ className: "text-ui-fg-muted hover:text-ui-fg-error p-1",
4266
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "w-4 h-4" })
4267
+ }
4268
+ ) })
4269
+ ] }),
4270
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
4271
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-[11px] text-ui-fg-subtle", children: "Description (Optional)" }),
4272
+ /* @__PURE__ */ jsxRuntime.jsx(
4273
+ ui.Input,
4274
+ {
4275
+ placeholder: "Optional item details / specifications",
4276
+ value: it.description,
4277
+ onChange: (e) => {
4278
+ const newItems = [...editForm.items];
4279
+ newItems[idx] = { ...newItems[idx], description: e.target.value };
4280
+ setEditForm({ ...editForm, items: newItems });
4281
+ },
4282
+ size: "small"
4283
+ }
4284
+ )
4285
+ ] })
4286
+ ]
4287
+ },
4288
+ idx
4289
+ )) }),
4290
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end items-center gap-4 py-2 border-t border-ui-border-base", children: [
4291
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-subtle", children: "Calculated Total:" }),
4292
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-sm font-semibold", children: formatCurrency(
4293
+ editForm.items.reduce(
4294
+ (sum, it) => sum + (Number(it.quantity) || 1) * (Number(it.unit_price) || 0),
4295
+ 0
4296
+ ),
4297
+ editingDoc.currency_code || "KES"
4298
+ ) })
4299
+ ] })
4300
+ ] }),
4301
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
4302
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4303
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Notes / Client Remarks" }),
4304
+ /* @__PURE__ */ jsxRuntime.jsx(
4305
+ ui.Textarea,
4306
+ {
4307
+ value: editForm.notes,
4308
+ onChange: (e) => setEditForm({ ...editForm, notes: e.target.value }),
4309
+ rows: 3,
4310
+ placeholder: "Notes visible on the document..."
4311
+ }
4312
+ )
4313
+ ] }),
4314
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4315
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "text-xs", children: "Terms & Conditions" }),
4316
+ /* @__PURE__ */ jsxRuntime.jsx(
4317
+ ui.Textarea,
4318
+ {
4319
+ value: editForm.terms,
4320
+ onChange: (e) => setEditForm({ ...editForm, terms: e.target.value }),
4321
+ rows: 3,
4322
+ placeholder: "Terms and conditions..."
4323
+ }
4324
+ )
4325
+ ] })
4326
+ ] })
4327
+ ] })
3980
4328
  ] }) })
3981
4329
  ] });
3982
4330
  };