diff --git a/adminapp/src/api.js b/adminapp/src/api.js
index 6cef54e7..323a542a 100644
--- a/adminapp/src/api.js
+++ b/adminapp/src/api.js
@@ -199,6 +199,8 @@ export default {
post(`/adminapi/v1/marketing_lists/${id}/destroy`, data, ...args),
rebuildMarketingList: ({ id, ...data }, ...args) =>
post(`/adminapi/v1/marketing_lists/${id}/rebuild`, data, ...args),
+ uploadingMarketingListCsv: ({ id, ...data }, ...args) =>
+ postForm(`/adminapi/v1/marketing_lists/${id}/upload_csv`, data, ...args),
getMarketingSmsBroadcasts: (data, ...args) =>
get(`/adminapi/v1/marketing_sms_broadcasts`, data, ...args),
diff --git a/adminapp/src/components/FileUploadInput.jsx b/adminapp/src/components/FileUploadInput.jsx
new file mode 100644
index 00000000..9795b173
--- /dev/null
+++ b/adminapp/src/components/FileUploadInput.jsx
@@ -0,0 +1,56 @@
+import useErrorSnackbar from "../hooks/useErrorSnackbar";
+import useToggle from "../shared/react/useToggle";
+import UploadFileIcon from "@mui/icons-material/UploadFile";
+import { Button, CircularProgress } from "@mui/material";
+import ButtonGroup from "@mui/material/ButtonGroup";
+import { useSnackbar } from "notistack";
+import React from "react";
+
+export default function FileUploadInput({ accept, label, onUpload }) {
+ label = label || "Choose File";
+ accept = accept || "*.*";
+ const [file, setFile] = React.useState("");
+ const uploading = useToggle();
+ const { enqueueErrorSnackbar } = useErrorSnackbar();
+ const { enqueueSnackbar } = useSnackbar();
+
+ function handleFileChange(e) {
+ setFile(e.target.files?.[0] ?? null);
+ }
+
+ async function handleUpload(e) {
+ e.preventDefault();
+ if (!file) {
+ return;
+ }
+ uploading.turnOn();
+
+ try {
+ await onUpload(file);
+ enqueueSnackbar(`Uploaded ${file.name}`, { variant: "success" });
+ setFile(null);
+ } catch (err) {
+ enqueueErrorSnackbar(err);
+ } finally {
+ uploading.turnOff();
+ }
+ }
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/adminapp/src/pages/MarketingListDetailPage.jsx b/adminapp/src/pages/MarketingListDetailPage.jsx
index d4a1a3d0..08ccea3b 100644
--- a/adminapp/src/pages/MarketingListDetailPage.jsx
+++ b/adminapp/src/pages/MarketingListDetailPage.jsx
@@ -1,5 +1,6 @@
import api from "../api";
import AdminLink from "../components/AdminLink";
+import FileUploadInput from "../components/FileUploadInput";
import RelatedListRemote from "../components/RelatedListRemote";
import ResourceDetail from "../components/ResourceDetail";
import resourceDetailCommonFields from "../components/resourceDetailCommonFields";
@@ -25,6 +26,14 @@ export default function MarketingListDetailPage() {
.finally(notBusy);
}
+ function handleCsvUpload(file, setModel) {
+ busy();
+ api
+ .uploadingMarketingListCsv({ id, file })
+ .then((r) => setModel(r.data))
+ .finally(notBusy);
+ }
+
if (isBusy) {
return ;
}
@@ -49,6 +58,11 @@ export default function MarketingListDetailPage() {
]}
>
{(model, setModel) => [
+ handleCsvUpload(f, setModel)}
+ />,
model.managed && (