Skip to content

Commit 00c960e

Browse files
committed
admin: delete historical price feature
1 parent ca9cfbc commit 00c960e

5 files changed

Lines changed: 281 additions & 2 deletions

File tree

apps/admin/src/lib/server-actions/dt/history-price.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,81 @@
33
import { getTradingCenters } from "@/lib/db-queries/trading-centers";
44
import { getMmDb, tdCollections } from "@/lib/db/config";
55
import mongoClient from "@/lib/db/mongodb";
6+
import { isStringEmpty } from "@/lib/utils";
67
import { auth } from "@mm-app/auth/server";
78
import { VeggiePrice } from "@mm-app/internal/api";
89
import { ObjectId } from "mongodb";
910
import { revalidatePath } from "next/cache";
1011
import { headers } from "next/headers";
1112

13+
export const deleteVeggieHistoryPrice = async (
14+
id: string,
15+
tradingCenter: string,
16+
) => {
17+
const session = await auth.api.getSession({
18+
headers: await headers(),
19+
});
20+
if (!session) {
21+
return {
22+
success: false,
23+
message: "Not logged in.",
24+
};
25+
}
26+
27+
if (isStringEmpty(id)) {
28+
return {
29+
success: false,
30+
message: "No ID provided",
31+
};
32+
}
33+
34+
// Get trading centers for validation
35+
const tradingCenters = await getTradingCenters();
36+
if (!tradingCenters.success) {
37+
return {
38+
success: false,
39+
message: "Failed to get trading centers list",
40+
};
41+
}
42+
43+
// Validate if trading center is valid
44+
const tradingCenterData = tradingCenters.data?.find(
45+
(tc) => tc.slug === tradingCenter,
46+
);
47+
if (!tradingCenterData) {
48+
return {
49+
success: false,
50+
message: "Trading center not found",
51+
};
52+
}
53+
54+
const db = mongoClient.db(getMmDb(tradingCenter));
55+
const historyPricesCol = db.collection(tdCollections.historyPrices);
56+
57+
try {
58+
const res = await historyPricesCol.deleteOne({
59+
_id: new ObjectId(id),
60+
});
61+
if (!res.acknowledged) {
62+
throw new Error("Failed to delete history price");
63+
}
64+
65+
revalidatePath("/dashboard/data-management/historical-prices");
66+
67+
return {
68+
success: true,
69+
message: "History price deleted successfully",
70+
};
71+
} catch (err) {
72+
console.error("Error deleting history price:", err);
73+
74+
return {
75+
success: false,
76+
error: "Failed to delete history price :> " + err,
77+
};
78+
}
79+
};
80+
1281
export const updateVeggieHistoryPrice = async (
1382
data: Partial<VeggiePrice>,
1483
tradingCenter: string,

apps/admin/src/modules/data-management/history-prices/actions-provider.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
useContext,
1010
useState,
1111
} from "react";
12+
import HistoryDelete from "./history-delete";
1213
import HistoryEdit from "./history-edit";
1314

1415
type Action =
@@ -17,6 +18,11 @@ type Action =
1718
veggiePrice: VeggiePrice;
1819
isOpen: true;
1920
}
21+
| {
22+
type: "delete";
23+
veggiePrice: VeggiePrice;
24+
isOpen: true;
25+
}
2026
| {
2127
isOpen: false;
2228
};
@@ -71,6 +77,8 @@ export default function HistoryActionsProvider(props: {
7177
{props.children}
7278

7379
<HistoryEdit />
80+
81+
<HistoryDelete />
7482
</context.Provider>
7583
);
7684
}

apps/admin/src/modules/data-management/history-prices/columns.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ import {
1111
DropdownMenuTrigger,
1212
} from "@mm-app/ui/components/dropdown-menu";
1313
import { ColumnDef } from "@tanstack/react-table";
14-
import { ArrowUpDownIcon, EditIcon, MoreHorizontalIcon } from "lucide-react";
14+
import {
15+
ArrowUpDownIcon,
16+
DeleteIcon,
17+
EditIcon,
18+
MoreHorizontalIcon,
19+
} from "lucide-react";
1520
import { useHistoryDataActions } from "./actions-provider";
1621

1722
export const historyPricesDTColumns: ColumnDef<VeggiePrice>[] = [
@@ -118,6 +123,18 @@ export const historyPricesDTColumns: ColumnDef<VeggiePrice>[] = [
118123
<EditIcon />
119124
Edit
120125
</DropdownMenuItem>
126+
<DropdownMenuItem
127+
onSelect={() => {
128+
setAction({
129+
type: "delete",
130+
isOpen: true,
131+
veggiePrice: row.original,
132+
});
133+
}}
134+
>
135+
<DeleteIcon />
136+
Delete
137+
</DropdownMenuItem>
121138
</DropdownMenuContent>
122139
</DropdownMenu>
123140
);
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
"use client";
2+
3+
import { deleteVeggieHistoryPrice } from "@/lib/server-actions/dt/history-price";
4+
import { zodResolver } from "@hookform/resolvers/zod";
5+
import { Button } from "@mm-app/ui/components/button";
6+
import {
7+
Dialog,
8+
DialogClose,
9+
DialogContent,
10+
DialogDescription,
11+
DialogHeader,
12+
DialogTitle,
13+
} from "@mm-app/ui/components/dialog";
14+
import {
15+
Form,
16+
FormControl,
17+
FormField,
18+
FormItem,
19+
FormLabel,
20+
} from "@mm-app/ui/components/form";
21+
import { Input } from "@mm-app/ui/components/input";
22+
import { useEffect, useState, useTransition } from "react";
23+
import { useForm } from "react-hook-form";
24+
import { toast } from "sonner";
25+
import { z } from "zod";
26+
import { useDataManagement } from "../dt-provider";
27+
import { useHistoryDataActions } from "./actions-provider";
28+
29+
const deleteFormSchema = z.object({
30+
_id: z.string(),
31+
id: z.string(),
32+
});
33+
34+
type DeleteForm = z.infer<typeof deleteFormSchema>;
35+
36+
export default function HistoryDelete() {
37+
const { tradingCenter } = useDataManagement();
38+
const { action, handleCloseAction } = useHistoryDataActions();
39+
40+
const [isSet, setIsSet] = useState(false);
41+
const [isProcessing, startTransition] = useTransition();
42+
43+
const form = useForm<DeleteForm>({
44+
resolver: zodResolver(deleteFormSchema),
45+
defaultValues: action.isOpen
46+
? {
47+
_id: action.veggiePrice._id,
48+
id: action.veggiePrice.id,
49+
}
50+
: {
51+
_id: "",
52+
id: "",
53+
},
54+
});
55+
56+
const handleSubmit = async (data: DeleteForm) => {
57+
if (!action.isOpen) return;
58+
59+
if (action.type !== "delete") {
60+
toast.error("Invalid action type for delete");
61+
return;
62+
}
63+
64+
if (!data.id) {
65+
toast.error("No ID provided for deletion");
66+
return;
67+
}
68+
69+
const process = toast.loading("Deleting historical price...");
70+
71+
startTransition(async () => {
72+
const res = await deleteVeggieHistoryPrice(data._id, tradingCenter);
73+
if (!res.success) {
74+
toast.error(res.message || "Failed to delete historical price", {
75+
id: process,
76+
});
77+
return;
78+
}
79+
80+
startTransition(() => {
81+
toast.success("Historical price deleted successfully", {
82+
id: process,
83+
});
84+
85+
handleCloseAction(false);
86+
});
87+
});
88+
};
89+
90+
useEffect(() => {
91+
if (isSet) return;
92+
93+
if (action.isOpen) {
94+
form.reset({
95+
_id: action.veggiePrice._id,
96+
id: action.veggiePrice.id,
97+
});
98+
setIsSet(true);
99+
}
100+
}, [action, form, isSet]);
101+
102+
return (
103+
<Dialog
104+
open={action.isOpen && action.type === "delete"}
105+
onOpenChange={(value) => !isProcessing && handleCloseAction(value)}
106+
>
107+
<DialogContent
108+
onInteractOutside={(e) => isProcessing && e.preventDefault()}
109+
className="max-w-xl"
110+
>
111+
<DialogHeader>
112+
<DialogTitle>Delete Price History</DialogTitle>
113+
<DialogDescription>
114+
Are you sure you want to delete this historical price?
115+
<br />
116+
This action cannot be undone.
117+
</DialogDescription>
118+
</DialogHeader>
119+
120+
{action.isOpen ? (
121+
<div className="border p-2 rounded-lg text-muted-foreground text-sm">
122+
<p>
123+
Vegetable: <strong>{action.veggiePrice.name}</strong>
124+
</p>
125+
<p>
126+
Date:{" "}
127+
<strong>
128+
{new Date(action.veggiePrice.dateUnix).toLocaleDateString(
129+
"en-US",
130+
{
131+
year: "numeric",
132+
month: "long",
133+
day: "numeric",
134+
},
135+
)}
136+
</strong>
137+
</p>
138+
</div>
139+
) : null}
140+
141+
<div>
142+
<Form {...form}>
143+
<form
144+
onSubmit={form.handleSubmit(handleSubmit)}
145+
className="space-y-4"
146+
>
147+
<input type="hidden" {...form.register("_id")} />
148+
149+
<FormField
150+
control={form.control}
151+
name="id"
152+
render={({ field }) => (
153+
<FormItem>
154+
<FormLabel>Vegetable ID</FormLabel>
155+
<FormControl>
156+
<Input
157+
{...field}
158+
readOnly
159+
placeholder="This is the ID of the vegetable"
160+
/>
161+
</FormControl>
162+
</FormItem>
163+
)}
164+
/>
165+
166+
<div className="flex items-center justify-end space-x-2">
167+
<DialogClose disabled={isProcessing} asChild>
168+
<Button variant="outline">Cancel</Button>
169+
</DialogClose>
170+
171+
<Button
172+
disabled={isProcessing}
173+
type="submit"
174+
variant={"destructive"}
175+
>
176+
{isProcessing ? "Deleting..." : "Delete"}
177+
</Button>
178+
</div>
179+
</form>
180+
</Form>
181+
</div>
182+
</DialogContent>
183+
</Dialog>
184+
);
185+
}

apps/admin/src/modules/data-management/history-prices/history-edit.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ export default function HistoryEdit() {
150150
className="max-w-xl"
151151
>
152152
<DialogHeader>
153-
<DialogTitle>Edit Vegetable</DialogTitle>
153+
<DialogTitle>Edit Historical Price</DialogTitle>
154154
<DialogDescription>
155155
For now, editing the name is not allowed.
156156
</DialogDescription>

0 commit comments

Comments
 (0)