@nebulit/embuilder 0.1.44 → 0.1.46

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.
@@ -1,224 +0,0 @@
1
- import { useState } from "react";
2
- import { DashboardLayout } from "@/components/layout/DashboardLayout";
3
- import { Button } from "@/components/ui/button";
4
- import { Input } from "@/components/ui/input";
5
- import { Label } from "@/components/ui/label";
6
- import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
7
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
8
- import { Card, CardContent } from "@/components/ui/card";
9
- import { Plus, Trash2, Download, FileText, Loader2 } from "lucide-react";
10
- import { Menu } from "@/types";
11
- import { Badge } from "@/components/ui/badge";
12
- import { toast } from "sonner";
13
- import { useMenus, useUploadMenu, useDeleteMenu } from "@/hooks/api";
14
- import { Skeleton } from "@/components/ui/skeleton";
15
-
16
- const documentTypes = [
17
- { value: "primary", label: "Primary" },
18
- { value: "secondary", label: "Secondary" },
19
- { value: "report", label: "Report" },
20
- { value: "template", label: "Template" },
21
- { value: "archive", label: "Archive" },
22
- { value: "other", label: "Other" },
23
- ];
24
-
25
- export default function Menus() {
26
- const { data: menus = [], isLoading, error } = useMenus();
27
- const uploadMenuMutation = useUploadMenu();
28
- const deleteMenuMutation = useDeleteMenu();
29
-
30
- const [isAddOpen, setIsAddOpen] = useState(false);
31
- const [formData, setFormData] = useState({
32
- menuName: "",
33
- menuType: "",
34
- file: "",
35
- });
36
-
37
- const handleAddMenu = async () => {
38
- if (!formData.menuName || !formData.menuType) {
39
- toast.error("Please fill in all required fields");
40
- return;
41
- }
42
-
43
- try {
44
- await uploadMenuMutation.mutateAsync({
45
- menuName: formData.menuName,
46
- menuType: formData.menuType,
47
- file: formData.file,
48
- restaurantId: "", // Will be set from context headers
49
- });
50
- setFormData({ menuName: "", menuType: "", file: "" });
51
- setIsAddOpen(false);
52
- toast.success("Document uploaded successfully");
53
- } catch (err) {
54
- toast.error(`Error: ${err instanceof Error ? err.message : "Unknown error"}`);
55
- }
56
- };
57
-
58
- const deleteMenu = async (menu: Menu) => {
59
- try {
60
- await deleteMenuMutation.mutateAsync(menu.menuId);
61
- toast.success(`"${menu.menuName}" deleted`);
62
- } catch (err) {
63
- toast.error(`Error: ${err instanceof Error ? err.message : "Unknown error"}`);
64
- }
65
- };
66
-
67
- const getTypeColor = (type: string) => {
68
- const colors: Record<string, string> = {
69
- primary: "bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-300",
70
- secondary: "bg-indigo-100 text-indigo-800 dark:bg-indigo-900/30 dark:text-indigo-300",
71
- report: "bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300",
72
- template: "bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-300",
73
- archive: "bg-pink-100 text-pink-800 dark:bg-pink-900/30 dark:text-pink-300",
74
- other: "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/30 dark:text-emerald-300",
75
- };
76
- return colors[type] || "bg-muted text-muted-foreground";
77
- };
78
-
79
- const getTypeLabel = (type: string) => {
80
- const docType = documentTypes.find((t) => t.value === type);
81
- return docType?.label || type;
82
- };
83
-
84
- if (error) {
85
- return (
86
- <DashboardLayout title="Documents" subtitle="Manage your documents">
87
- <div className="flex flex-col items-center justify-center py-12 text-center">
88
- <p className="text-destructive">Error loading: {error.message}</p>
89
- <p className="mt-2 text-sm text-muted-foreground">
90
- Check the API settings (Settings icon in the header)
91
- </p>
92
- </div>
93
- </DashboardLayout>
94
- );
95
- }
96
-
97
- return (
98
- <DashboardLayout title="Documents" subtitle="Manage your documents">
99
- <div className="mb-6 flex items-center justify-between">
100
- <p className="text-muted-foreground">
101
- {isLoading ? "Loading..." : `${menus.length} document${menus.length !== 1 ? "s" : ""} uploaded`}
102
- </p>
103
- <Dialog open={isAddOpen} onOpenChange={setIsAddOpen}>
104
- <DialogTrigger asChild>
105
- <Button>
106
- <Plus className="mr-2 h-4 w-4" />
107
- Upload document
108
- </Button>
109
- </DialogTrigger>
110
- <DialogContent>
111
- <DialogHeader>
112
- <DialogTitle>Upload new document</DialogTitle>
113
- </DialogHeader>
114
- <div className="space-y-4 pt-4">
115
- <div>
116
- <Label htmlFor="menuName">Document name *</Label>
117
- <Input
118
- id="menuName"
119
- value={formData.menuName}
120
- onChange={(e) => setFormData({ ...formData, menuName: e.target.value })}
121
- placeholder="e.g. Q1 Report"
122
- />
123
- </div>
124
- <div>
125
- <Label htmlFor="menuType">Document type *</Label>
126
- <Select
127
- value={formData.menuType}
128
- onValueChange={(value) => setFormData({ ...formData, menuType: value })}
129
- >
130
- <SelectTrigger>
131
- <SelectValue placeholder="Select type" />
132
- </SelectTrigger>
133
- <SelectContent>
134
- {documentTypes.map((type) => (
135
- <SelectItem key={type.value} value={type.value}>
136
- {type.label}
137
- </SelectItem>
138
- ))}
139
- </SelectContent>
140
- </Select>
141
- </div>
142
- <div>
143
- <Label htmlFor="file">File</Label>
144
- <Input
145
- id="file"
146
- type="file"
147
- accept=".pdf,.doc,.docx"
148
- onChange={(e) => setFormData({ ...formData, file: e.target.files?.[0]?.name || "" })}
149
- />
150
- <p className="mt-1 text-xs text-muted-foreground">PDF or Word documents are accepted</p>
151
- </div>
152
- <Button
153
- onClick={handleAddMenu}
154
- className="w-full"
155
- disabled={uploadMenuMutation.isPending}
156
- >
157
- {uploadMenuMutation.isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
158
- Upload document
159
- </Button>
160
- </div>
161
- </DialogContent>
162
- </Dialog>
163
- </div>
164
-
165
- {isLoading ? (
166
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
167
- {[1, 2, 3].map((i) => (
168
- <Card key={i}>
169
- <CardContent className="p-4">
170
- <div className="flex items-center gap-3">
171
- <Skeleton className="h-12 w-12 rounded-lg" />
172
- <div>
173
- <Skeleton className="h-4 w-32 mb-2" />
174
- <Skeleton className="h-3 w-24" />
175
- </div>
176
- </div>
177
- </CardContent>
178
- </Card>
179
- ))}
180
- </div>
181
- ) : (
182
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
183
- {menus.map((menu) => (
184
- <Card key={menu.menuId} className="group transition-shadow hover:shadow-md animate-fade-in">
185
- <CardContent className="p-4">
186
- <div className="flex items-start justify-between">
187
- <div className="flex items-center gap-3">
188
- <div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10">
189
- <FileText className="h-6 w-6 text-primary" />
190
- </div>
191
- <div>
192
- <h3 className="font-medium text-foreground">{menu.menuName}</h3>
193
- <p className="text-sm text-muted-foreground">{menu.file || "No file"}</p>
194
- </div>
195
- </div>
196
- </div>
197
-
198
- <div className="mt-4 flex items-center justify-between">
199
- <Badge className={getTypeColor(menu.menuType)}>
200
- {getTypeLabel(menu.menuType)}
201
- </Badge>
202
- <div className="flex gap-1 opacity-0 transition-opacity group-hover:opacity-100">
203
- <Button variant="ghost" size="icon" className="h-8 w-8">
204
- <Download className="h-4 w-4" />
205
- </Button>
206
- <Button
207
- variant="ghost"
208
- size="icon"
209
- className="h-8 w-8 text-destructive hover:bg-destructive hover:text-destructive-foreground"
210
- onClick={() => deleteMenu(menu)}
211
- disabled={deleteMenuMutation.isPending}
212
- >
213
- <Trash2 className="h-4 w-4" />
214
- </Button>
215
- </div>
216
- </div>
217
- </CardContent>
218
- </Card>
219
- ))}
220
- </div>
221
- )}
222
- </DashboardLayout>
223
- );
224
- }