From 64e06bdaff46f515a6cd67ac122f5adabf7840fe Mon Sep 17 00:00:00 2001 From: Xynox Date: Fri, 8 Nov 2024 13:21:49 +0530 Subject: [PATCH] feat(*): final app --- .../app/blog-markdown-generator.tsx | 233 ++++++++++++++++++ blog-markdown-generator/app/page.tsx | 104 +------- .../components/ui/toast.tsx | 129 ++++++++++ .../components/ui/toaster.tsx | 35 +++ blog-markdown-generator/hooks/use-toast.ts | 194 +++++++++++++++ 5 files changed, 597 insertions(+), 98 deletions(-) create mode 100644 blog-markdown-generator/app/blog-markdown-generator.tsx create mode 100644 blog-markdown-generator/components/ui/toast.tsx create mode 100644 blog-markdown-generator/components/ui/toaster.tsx create mode 100644 blog-markdown-generator/hooks/use-toast.ts diff --git a/blog-markdown-generator/app/blog-markdown-generator.tsx b/blog-markdown-generator/app/blog-markdown-generator.tsx new file mode 100644 index 0000000..2489bf5 --- /dev/null +++ b/blog-markdown-generator/app/blog-markdown-generator.tsx @@ -0,0 +1,233 @@ +'use client' + +import { useState, useEffect } from 'react' +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Textarea } from "@/components/ui/textarea" +import { Label } from "@/components/ui/label" +import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { Copy, Check } from 'lucide-react' +import { useToast } from "../hooks/use-toast" + +export default function BlogMarkdownGenerator() { + const [mounted, setMounted] = useState(false) + const [blogData, setBlogData] = useState({ + title: '', + author: '', + coverImage: '', + introduction: '', + sections: [{ title: '', content: '' }], + conclusion: '', + authorBio: '', + twitterHandle: '', + linkedinProfile: '', + githubUsername: '', + }) + const [generatedMarkdown, setGeneratedMarkdown] = useState('') + const [isCopied, setIsCopied] = useState(false) + const { toast } = useToast() + + useEffect(() => { + setMounted(true) + }, []) + + if (!mounted) { + return ( + + + Loading... + + +
+
Loading editor...
+
+
+
+ ) + } + + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value } = e.target + setBlogData(prev => ({ ...prev, [name]: value })) + } + + const handleSectionChange = (index: number, field: 'title' | 'content', value: string) => { + setBlogData(prev => ({ + ...prev, + sections: prev.sections.map((section, i) => + i === index ? { ...section, [field]: value } : section + ) + })) + } + + const addSection = () => { + setBlogData(prev => ({ + ...prev, + sections: [...prev.sections, { title: '', content: '' }] + })) + } + + const generateMarkdown = () => { + let markdown = `# ${blogData.title}\n\n` + markdown += `*By [${blogData.author}](https://your-website.com)*\n\n` + markdown += blogData.coverImage ? `![Blog Cover Image](${blogData.coverImage})\n\n` : '' + + markdown += `## Table of Contents\n` + markdown += `- [Introduction](#introduction)\n` + blogData.sections.forEach((section, index) => { + if (section.title) { + markdown += `- [${section.title}](#section-${index + 1}-${section.title.toLowerCase().replace(/\s+/g, '-')})\n` + } + }) + markdown += `- [Conclusion](#conclusion)\n\n` + + markdown += `## Introduction\n\n${blogData.introduction}\n\n` + + blogData.sections.forEach((section, index) => { + if (section.title) { + markdown += `## Section ${index + 1}: ${section.title}\n\n${section.content}\n\n` + } + }) + + markdown += `## Conclusion\n\n${blogData.conclusion}\n\n---\n\n` + + if (blogData.author || blogData.authorBio) { + markdown += `### About the Author\n\n` + markdown += `**${blogData.author}** ${blogData.authorBio}\n\n` + } + + if (blogData.twitterHandle || blogData.linkedinProfile || blogData.githubUsername) { + markdown += `Connect with me:\n` + if (blogData.twitterHandle) markdown += `- [Twitter](https://twitter.com/${blogData.twitterHandle})\n` + if (blogData.linkedinProfile) markdown += `- [LinkedIn](${blogData.linkedinProfile})\n` + if (blogData.githubUsername) markdown += `- [GitHub](https://github.com/${blogData.githubUsername})\n` + markdown += '\n' + } + + markdown += `---\n\n` + if (blogData.twitterHandle) { + markdown += `*Did you find this blog post helpful? [Share it on Twitter](https://twitter.com/intent/tweet?text=Check%20out%20this%20amazing%20blog%20post%20by%20@${blogData.twitterHandle}:%20https://gist.github.com/your-gist-url)*\n\n` + } + markdown += `*For more content like this, [subscribe to my newsletter](https://your-newsletter-url.com).*` + + setGeneratedMarkdown(markdown) + } + + const copyToClipboard = async () => { + try { + await navigator.clipboard.writeText(generatedMarkdown) + setIsCopied(true) + toast({ + title: "Copied!", + description: "Markdown has been copied to clipboard.", + }) + setTimeout(() => setIsCopied(false), 2000) + } catch (err) { + console.error('Failed to copy text: ', err) + toast({ + title: "Error", + description: "Failed to copy. Please try again.", + variant: "destructive", + }) + } + } + + return ( + + + Blog Markdown Generator + + + + + Input + Preview + + +
+
+ + +
+
+ + +
+
+ + +
+
+ +