create-nextblock 0.2.51 → 0.2.53

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nextblock",
3
- "version": "0.2.51",
3
+ "version": "0.2.53",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1,53 @@
1
+ "use server";
2
+
3
+ import { sendEmail } from "./email";
4
+
5
+
6
+ interface FeedbackData {
7
+ subject: string;
8
+ message: string;
9
+ userEmail: string;
10
+ userName?: string;
11
+ url?: string;
12
+ }
13
+
14
+ export async function submitFeedback(data: FeedbackData) {
15
+ try {
16
+ // If we have an email config, we use it. If not, we might fail or log.
17
+ // sendEmail throws if not configured.
18
+
19
+ const { subject, message, userEmail, userName, url } = data;
20
+
21
+ const htmlContent = `
22
+ <h2>New Feedback Received</h2>
23
+ <p><strong>From:</strong> ${userName || 'Unknown'} (${userEmail})</p>
24
+ <p><strong>Subject:</strong> ${subject}</p>
25
+ <p><strong>URL:</strong> ${url || 'N/A'}</p>
26
+ <br/>
27
+ <h3>Message:</h3>
28
+ <p style="white-space: pre-wrap;">${message}</p>
29
+ `;
30
+
31
+ const textContent = `
32
+ New Feedback Received
33
+ From: ${userName || 'Unknown'} (${userEmail})
34
+ Subject: ${subject}
35
+ URL: ${url || 'N/A'}
36
+
37
+ Message:
38
+ ${message}
39
+ `;
40
+
41
+ await sendEmail({
42
+ to: "feedback@nextblock.ca", // Fixed typo from 'feeedback'
43
+ subject: `[CMS Feedback] ${subject}`,
44
+ text: textContent,
45
+ html: htmlContent,
46
+ });
47
+
48
+ return { success: true };
49
+ } catch (error) {
50
+ console.error("Failed to submit feedback:", error);
51
+ return { success: false, error: "Failed to send feedback email." };
52
+ }
53
+ }
@@ -5,15 +5,16 @@ import React, { type ReactNode, useEffect } from "react"
5
5
  import { useAuth } from "@/context/AuthContext"
6
6
  import { useRouter, usePathname } from "next/navigation" // Import usePathname
7
7
  import Link from "next/link"
8
- import {
9
- LayoutDashboard, FileText, PenTool, Users, Settings, ChevronRight, LogOut, Menu, ListTree, Image as ImageIconLucide, X, Languages as LanguagesIconLucide, MessageSquare,
10
- Copyright as CopyrightIcon,
11
- } from "lucide-react"
12
- import { Button } from "@nextblock-cms/ui"
13
- import { Avatar, AvatarFallback, AvatarImage } from "@nextblock-cms/ui"
14
- import { cn } from "@nextblock-cms/utils"
15
- import { signOutAction } from "@/app/actions";
16
- import Image from "next/image";
8
+ import {
9
+ LayoutDashboard, FileText, PenTool, Users, Settings, ChevronRight, LogOut, Menu, ListTree, Image as ImageIconLucide, X, Languages as LanguagesIconLucide, MessageSquare,
10
+ Copyright as CopyrightIcon,
11
+ } from "lucide-react"
12
+ import { Button } from "@nextblock-cms/ui"
13
+ import { Avatar, AvatarFallback, AvatarImage } from "@nextblock-cms/ui"
14
+ import { cn } from "@nextblock-cms/utils"
15
+ import { signOutAction } from "@/app/actions";
16
+ import Image from "next/image";
17
+ import { FeedbackModal } from "./components/FeedbackModal";
17
18
 
18
19
  const LoadingSpinner = () => (
19
20
  <div className="flex justify-center items-center h-full w-full py-20">
@@ -211,21 +212,21 @@ export default function CmsClientLayout({ children }: { children: ReactNode }) {
211
212
  )}
212
213
  >
213
214
  <div className="flex flex-col h-full">
214
- <div className="p-4 border-b dark:border-slate-700/60 h-16 flex items-center shrink-0">
215
- <Link href="/cms/dashboard" className="flex items-center gap-2 px-2">
216
- <Image
217
- src="/images/nextblock-logo-small.webp"
218
- alt="Nextblock logo"
219
- width={32}
220
- height={32}
221
- className="h-8 w-auto"
222
- priority
223
- />
224
- <h2 className="text-xl font-bold text-foreground">
225
- Nextblock CMS
226
- </h2>
227
- </Link>
228
- </div>
215
+ <div className="p-4 border-b dark:border-slate-700/60 h-16 flex items-center shrink-0">
216
+ <Link href="/cms/dashboard" className="flex items-center gap-2 px-2">
217
+ <Image
218
+ src="/images/nextblock-logo-small.webp"
219
+ alt="Nextblock logo"
220
+ width={32}
221
+ height={32}
222
+ className="h-8 w-auto"
223
+ priority
224
+ />
225
+ <h2 className="text-xl font-bold text-foreground">
226
+ Nextblock CMS
227
+ </h2>
228
+ </Link>
229
+ </div>
229
230
 
230
231
  <nav className="px-3 py-4 flex-1 overflow-y-auto">
231
232
  <ul className="space-y-1.5">
@@ -280,6 +281,11 @@ export default function CmsClientLayout({ children }: { children: ReactNode }) {
280
281
  </ul>
281
282
  </nav>
282
283
 
284
+
285
+ <div className="p-3 pb-0">
286
+ <FeedbackModal />
287
+ </div>
288
+
283
289
  <div className="mt-auto p-3 border-t border-slate-200 dark:border-slate-700/60 shrink-0">
284
290
  <div className="flex items-center gap-3">
285
291
  <Avatar className="h-10 w-10 border">
@@ -324,4 +330,4 @@ export default function CmsClientLayout({ children }: { children: ReactNode }) {
324
330
  )}
325
331
  </div>
326
332
  )
327
- }
333
+ }
@@ -0,0 +1,134 @@
1
+ "use client"
2
+
3
+ import { useState } from "react"
4
+ import { useAuth } from "@/context/AuthContext"
5
+ import { usePathname } from "next/navigation"
6
+ import { toast } from "react-hot-toast"
7
+ import { submitFeedback } from "@/app/actions/feedback"
8
+ import {
9
+ Dialog,
10
+ DialogContent,
11
+ DialogDescription,
12
+ DialogHeader,
13
+ DialogTitle,
14
+ DialogTrigger,
15
+ DialogFooter,
16
+ Button,
17
+ Textarea,
18
+ Label,
19
+ Select,
20
+ SelectContent,
21
+ SelectItem,
22
+ SelectTrigger,
23
+ SelectValue
24
+ } from "@nextblock-cms/ui"
25
+ import { MessageSquarePlus, Loader2 } from "lucide-react"
26
+ import { cn } from "@nextblock-cms/utils"
27
+
28
+ interface FeedbackModalProps {
29
+ sidebarOpen?: boolean
30
+ }
31
+
32
+ export function FeedbackModal({ sidebarOpen = true }: FeedbackModalProps) {
33
+ const [open, setOpen] = useState(false)
34
+ const [loading, setLoading] = useState(false)
35
+ const [subject, setSubject] = useState("suggestion")
36
+ const [message, setMessage] = useState("")
37
+ const { user, profile } = useAuth()
38
+ const pathname = usePathname()
39
+
40
+ const handleSubmit = async (e: React.FormEvent) => {
41
+ e.preventDefault()
42
+ if (!message.trim()) {
43
+ toast.error("Please enter a message")
44
+ return
45
+ }
46
+
47
+ setLoading(true)
48
+ try {
49
+ const result = await submitFeedback({
50
+ subject,
51
+ message,
52
+ userEmail: user?.email || "unknown@example.com",
53
+ userName: profile?.full_name || user?.email || "Unknown",
54
+ url: pathname,
55
+ })
56
+
57
+ if (result.success) {
58
+ toast.success("Feedback sent! Thank you.")
59
+ setOpen(false)
60
+ setMessage("")
61
+ setSubject("suggestion")
62
+ } else {
63
+ toast.error("Failed to send feedback. Please try again.")
64
+ }
65
+ } catch (error) {
66
+ console.error(error)
67
+ toast.error("An error occurred.")
68
+ } finally {
69
+ setLoading(false)
70
+ }
71
+ }
72
+
73
+ return (
74
+ <Dialog open={open} onOpenChange={setOpen}>
75
+ <DialogTrigger asChild>
76
+ <button
77
+ className={cn(
78
+ "flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-all w-full text-left",
79
+ "text-slate-600 hover:text-primary hover:bg-primary/5 dark:text-slate-300 dark:hover:bg-primary/10"
80
+ )}
81
+ >
82
+ <MessageSquarePlus className="h-5 w-5" />
83
+ {sidebarOpen && <span>Feedback</span>}
84
+ </button>
85
+ </DialogTrigger>
86
+ <DialogContent className="sm:max-w-[425px]">
87
+ <DialogHeader>
88
+ <DialogTitle>Send Feedback</DialogTitle>
89
+ <DialogDescription>
90
+ Help us improve the CMS. Report bugs or suggest features.
91
+ </DialogDescription>
92
+ </DialogHeader>
93
+ <form onSubmit={handleSubmit} className="grid gap-4 py-4">
94
+ <div className="grid grid-cols-4 items-center gap-4">
95
+ <Label htmlFor="subject" className="text-right">
96
+ Type
97
+ </Label>
98
+ <div className="col-span-3">
99
+ <Select value={subject} onValueChange={setSubject}>
100
+ <SelectTrigger>
101
+ <SelectValue placeholder="Select a subject" />
102
+ </SelectTrigger>
103
+ <SelectContent>
104
+ <SelectItem value="suggestion">Suggestion</SelectItem>
105
+ <SelectItem value="bug">Bug Report</SelectItem>
106
+ <SelectItem value="feature">Feature Request</SelectItem>
107
+ <SelectItem value="other">Other</SelectItem>
108
+ </SelectContent>
109
+ </Select>
110
+ </div>
111
+ </div>
112
+ <div className="grid grid-cols-4 items-start gap-4">
113
+ <Label htmlFor="message" className="text-right pt-2">
114
+ Message
115
+ </Label>
116
+ <Textarea
117
+ id="message"
118
+ value={message}
119
+ onChange={(e) => setMessage(e.target.value)}
120
+ placeholder="Tell us what you think..."
121
+ className="col-span-3 min-h-[100px]"
122
+ />
123
+ </div>
124
+ <DialogFooter>
125
+ <Button type="submit" disabled={loading}>
126
+ {loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
127
+ Send Feedback
128
+ </Button>
129
+ </DialogFooter>
130
+ </form>
131
+ </DialogContent>
132
+ </Dialog>
133
+ )
134
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextblock-cms/template",
3
- "version": "0.2.29",
3
+ "version": "0.2.31",
4
4
  "private": true,
5
5
  "scripts": {
6
6
  "dev": "next dev",