diff --git a/apps/web/next.config.js b/apps/web/next.config.js index 3819a18b..365d7efc 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -10,6 +10,10 @@ const nextConfig = { protocol: "https", hostname: "lh3.googleusercontent.com", }, + { + protocol: "https", + hostname: "images.unsplash.com", + }, ], }, }; diff --git a/apps/web/package.json b/apps/web/package.json index 77494fc3..82c6350e 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -15,6 +15,7 @@ "@radix-ui/react-checkbox": "^1.1.2", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-radio-group": "^1.2.1", + "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.1.0", "@tanstack/react-query": "^5.90.2", "@trpc/client": "^11.6.0", diff --git a/apps/web/src/app/(main)/dashboard/newsletters/[id]/page.tsx b/apps/web/src/app/(main)/dashboard/newsletters/[id]/page.tsx new file mode 100644 index 00000000..86758a97 --- /dev/null +++ b/apps/web/src/app/(main)/dashboard/newsletters/[id]/page.tsx @@ -0,0 +1,157 @@ +"use client"; + +import { useParams } from "next/navigation"; +import Link from "next/link"; +import { newsletters } from "../data/newsletters"; +import { Calendar, ArrowLeft } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import Image from "next/image"; +import { GeistSans } from "geist/font/sans"; + +const renderContent = (text: string) => { + const parts = text.split(/(https?:\/\/[^\s]+)/g); + + return parts.map((part, i) => { + if (part.startsWith('http://') || part.startsWith('https://')) { + return ( + + {part} + + ); + } + return {part}; + }); +}; + +const ContentImage = ({ src, alt }: { src: string; alt: string }) => ( +
+ {alt} +
+); + +export default function NewsletterPage() { + const params = useParams(); + const id = parseInt(params.id as string); + const newsletter = newsletters.find((n) => n.id === id); + + if (!newsletter) { + return ( +
+
+

+ Newsletter not found +

+ + + +
+
+ ); + } + + const paragraphs = newsletter.content.split('\n\n'); + + return ( +
+
+ + + + +
+ {newsletter.image && ( +
+ {newsletter.title} +
+ )} + +

+ {newsletter.title} +

+ +
+
+ + {newsletter.date} +
+ by {newsletter.author} +
+ + {newsletter.preview && ( +

+ {newsletter.preview} +

+ )} +
+ +
+ +
+
+ {paragraphs.map((paragraph, index) => ( +
+

+ {renderContent(paragraph)} +

+ {newsletter.contentImages?.[0] && index === 1 && ( + + )} + {newsletter.contentImages?.[1] && index === 3 && ( + + )} +
+ ))} +
+
+ + {newsletter.takeaways && newsletter.takeaways.length > 0 && ( +
+

+ Key Takeaways +

+
    + {newsletter.takeaways.map((takeaway, index) => ( +
  • + {takeaway} +
  • + ))} +
+
+ )} + +
+ + + +
+
+
+ ); +} diff --git a/apps/web/src/app/(main)/dashboard/newsletters/data/newsletters.ts b/apps/web/src/app/(main)/dashboard/newsletters/data/newsletters.ts new file mode 100644 index 00000000..378e95ce --- /dev/null +++ b/apps/web/src/app/(main)/dashboard/newsletters/data/newsletters.ts @@ -0,0 +1,118 @@ +import type { Newsletter } from "@/types/newsletter"; + +export const newsletters: Newsletter[] = [ + { + id: 1, + title: "november product updates: new ai features and performance improvements", + date: "Nov 15, 2024", + author: "ajeet", + preview: "exciting new features including enhanced ai capabilities, faster load times, and improved user experience across the platform.", + content: "hey opensox community! we've been working hard this month to bring you some incredible updates that will transform how you use our platform. we're excited to announce several major improvements to opensox.ai that our pro users have been requesting.\n\nour ai engine is now 3x faster with improved accuracy. you'll notice significantly better results across all tasks, from content generation to data analysis. we've completely rebuilt our neural network architecture from the ground up, implementing state-of-the-art algorithms that reduce latency while maintaining the highest quality outputs. the new system can handle complex queries in milliseconds, making real-time ai interactions seamless. learn more at https://opensox.ai/ai-features.\n\nwe've completely redesigned the dashboard to make navigation more intuitive. the new interface puts your most-used features front and center. we've conducted extensive user research and gathered feedback from thousands of users to create an experience that feels natural and efficient. the new layout reduces the number of clicks needed to access key features by 60%, saving you valuable time. check it out at https://opensox.ai/dashboard.\n\npage load times are down by 40% across the platform. we've optimized our infrastructure to ensure you get the fastest possible experience. our engineering team has implemented advanced caching strategies, optimized database queries, and upgraded our CDN infrastructure. these improvements mean faster page loads, smoother interactions, and a better overall experience. see our performance metrics at https://opensox.ai/performance.\n\nthanks for being part of the opensox community. stay tuned for more updates next month!", + contentImages: [ + "https://images.unsplash.com/photo-1485827404703-89b55fcc595e?w=1200&h=600&fit=crop&q=80", + "https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=1200&h=600&fit=crop&q=80" + ], + image: "https://images.unsplash.com/photo-1677442136019-21780ecad995?w=1200&h=600&fit=crop&q=80", + takeaways: [ + "70% faster response times with improved accuracy", + "completely redesigned dashboard with intuitive navigation", + "40% reduction in page load times across the platform", + "enhanced ai capabilities for better content generation and data analysis" + ] + }, + { + id: 2, + title: "october community highlights: celebrating our pro users", + date: "Oct 20, 2024", + author: "ajeet", + preview: "this month we're spotlighting amazing projects built by our community and sharing tips from power users.", + content: "october has been an incredible month for the opensox community. let's celebrate some amazing achievements! we've seen some truly innovative uses of opensox.ai this month. from startups automating their workflows to enterprises scaling their operations.\n\na fintech startup used opensox to automate their customer onboarding process, reducing processing time from 2 hours to just 5 minutes. incredible work! they integrated our api into their existing systems and saw immediate improvements in efficiency. the automation handles document verification, background checks, and account setup seamlessly. read the full case study at https://opensox.ai/case-studies/fintech-automation.\n\nwe've also seen amazing projects from our developer community. one team built a content moderation system that processes thousands of posts per minute with 99.9% accuracy. another created an intelligent customer support bot that reduced response times by 80%. these success stories inspire us to keep building better tools.\n\nwe're working on some exciting features for november. expect major updates to our api, new integrations, and enhanced collaboration tools. we're adding support for more programming languages, expanding our webhook capabilities, and introducing team collaboration features that will make it easier to work together on projects. browse our templates at https://opensox.ai/templates. join our community forum at https://community.opensox.ai.\n\nthank you for making opensox.ai the best ai platform for professionals. see you next month!", + contentImages: [ + "https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=1200&h=600&fit=crop&q=80", + "https://images.unsplash.com/photo-1552664730-d307ca884978?w=1200&h=600&fit=crop&q=80" + ], + image: "https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=1200&h=600&fit=crop&q=80", + takeaways: [ + "use custom templates to save time on repetitive tasks", + "leverage batch processing for handling large datasets", + "set up webhooks for real-time integrations", + "join our community forum for tips and support" + ] + }, + { + id: 3, + title: "getting started with opensox.ai: a guide for new pro users", + date: "Sep 15, 2024", + author: "ajeet", + preview: "welcome to opensox! this guide will help you make the most of your pro subscription with tips, tricks, and best practices.", + content: "welcome to opensox.ai! we're thrilled to have you as a pro user. this guide will help you unlock the full potential of our platform. opensox.ai is built for professionals who need reliable, fast, and accurate ai capabilities. whether you're a developer, content creator, or business analyst, we've got you covered.\n\nwe offer enterprise-grade security and privacy, lightning-fast api responses, 99.9% uptime guarantee, and dedicated support for pro users. our infrastructure is built on industry-leading cloud providers with redundant systems across multiple regions. we encrypt all data in transit and at rest, ensuring your information is always protected. our api is designed for scale, handling millions of requests per day with sub-100ms response times.\n\ngetting started is easy. first, complete your profile at https://opensox.ai/profile. this helps us personalize your experience and provide better recommendations. verify your email to unlock all features and ensure account security.\n\nnext, try your first api call at https://opensox.ai/playground. our interactive playground lets you test api endpoints without writing any code. you can experiment with different parameters, see real-time results, and copy code snippets in multiple programming languages. it's the perfect way to learn how our api works.\n\nfor more advanced features, check out our comprehensive documentation at https://docs.opensox.ai. we have detailed guides for every endpoint, code examples in popular languages, and best practices for building production applications. our documentation is constantly updated with the latest features and improvements.\n\nour support team is here for you 24/7. reach out anytime via email, chat, or our community forum. we're committed to helping you succeed with opensox.ai. we can't wait to see what you'll build with opensox.ai. happy coding!", + contentImages: [ + "https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=1200&h=600&fit=crop&q=80", + "https://images.unsplash.com/photo-1551650975-87deedd944c3?w=1200&h=600&fit=crop&q=80" + ], + image: "https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=1200&h=600&fit=crop&q=80", + takeaways: [ + "complete your profile and verify your email", + "explore the dashboard and familiarize yourself with key features", + "try your first api call at the playground or use our web interface", + "check out our documentation for advanced features" + ] + }, + { + id: 4, + title: "december security updates and new api endpoints", + date: "Dec 10, 2024", + author: "ajeet", + preview: "enhanced security features and new api endpoints to make your integrations more powerful and secure.", + content: "we're excited to announce major security enhancements and new api endpoints this december. security is our top priority, and we've implemented advanced encryption protocols to protect your data.\n\nall api communications now use end-to-end encryption with tls 1.3, the latest and most secure protocol available. we've also implemented certificate pinning and perfect forward secrecy to ensure your data remains protected even in the event of a security breach. our security team conducts regular penetration testing and security audits to identify and fix vulnerabilities before they can be exploited.\n\nwe've also added new endpoints for batch processing and real-time analytics. the batch processing endpoint allows you to process thousands of requests efficiently, reducing api calls and improving performance. the real-time analytics endpoint provides instant insights into your data, enabling you to make decisions faster. these updates make it easier to build scalable applications with opensox.ai.\n\nour new api version includes improved error handling, better rate limiting, and enhanced monitoring capabilities. developers can now track their api usage in real-time, set up custom alerts, and receive detailed analytics about their integrations. learn about our security features at https://docs.opensox.ai/security. explore the new api endpoints at https://docs.opensox.ai/api. test them out in our playground at https://opensox.ai/playground.\n\nwe're committed to keeping your data safe while providing powerful tools for your projects. security is not a one-time effort but an ongoing commitment, and we're dedicated to maintaining the highest standards.", + contentImages: [ + "https://images.unsplash.com/photo-1563013544-824ae1b704d3?w=1200&h=600&fit=crop&q=80", + "https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=1200&h=600&fit=crop&q=80" + ], + image: "https://images.unsplash.com/photo-1563013544-824ae1b704d3?w=1200&h=600&fit=crop&q=80", + takeaways: [ + "advanced encryption protocols for all api communications", + "new batch processing endpoints for scalable applications", + "real-time analytics endpoints for instant insights", + "enhanced security documentation and best practices" + ] + }, + { + id: 5, + title: "august launch: opensox.ai is now live", + date: "Aug 1, 2024", + author: "ajeet", + preview: "we're thrilled to announce the official launch of opensox.ai, your new ai-powered platform for professionals.", + content: "after months of development and testing, we're excited to officially launch opensox.ai! this platform has been built from the ground up to provide professionals with powerful ai capabilities. whether you're building applications, creating content, or analyzing data, opensox.ai has the tools you need.\n\nour journey began with a simple vision: to make ai accessible and powerful for everyone. we've spent countless hours refining our algorithms, optimizing performance, and building an intuitive interface. the result is a platform that combines cutting-edge ai technology with ease of use.\n\nwe've started with a solid foundation featuring core ai capabilities, robust api infrastructure, and comprehensive documentation. but we're just getting started. we have an exciting roadmap ahead with features like custom model training, advanced analytics, and team collaboration tools. our team is constantly working on improvements and new features based on user feedback.\n\nwe're building a community of developers, creators, and innovators who are pushing the boundaries of what's possible with ai. join us on this journey at https://opensox.ai. sign up for your account at https://opensox.ai/signup. read our launch blog post at https://blog.opensox.ai/launch.\n\nwe can't wait to see what you'll build with opensox.ai! the possibilities are endless, and we're here to support you every step of the way.", + contentImages: [ + "https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=1200&h=600&fit=crop&q=80", + "https://images.unsplash.com/photo-1552664730-d307ca884978?w=1200&h=600&fit=crop&q=80" + ], + image: "https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=1200&h=600&fit=crop&q=80", + takeaways: [ + "official launch of opensox.ai platform", + "powerful ai capabilities for professionals", + "solid foundation for future growth", + "join us on this exciting journey" + ] + }, + { + id: 6, + title: "july beta program: thank you to our early adopters", + date: "Jul 20, 2024", + author: "ajeet", + preview: "a heartfelt thank you to all our beta testers who helped shape opensox.ai into what it is today.", + content: "we want to extend a huge thank you to all our beta testers! your feedback has been invaluable in shaping opensox.ai. during the beta period, we received thousands of suggestions and implemented many of your ideas. the platform is stronger because of your input.\n\nover the past few months, we've worked closely with our beta community to refine every aspect of the platform. your real-world use cases have revealed insights we never would have discovered on our own. from edge cases to performance optimizations, your feedback has been instrumental in making opensox.ai production-ready.\n\nwe've learned so much from your use cases and have built features specifically based on your needs. many of the features you see today were directly inspired by beta tester requests. the batch processing capabilities, improved error messages, and enhanced documentation all came from your suggestions.\n\nas we move forward, we want to keep this collaborative spirit alive. your voice matters, and we'll continue to listen and iterate based on your feedback. check out what's new at https://opensox.ai/features. share your feedback at https://opensox.ai/feedback. join our beta community at https://community.opensox.ai.\n\nthank you for being part of this journey with us! without you, opensox.ai wouldn't be what it is today.", + contentImages: [ + "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?w=1200&h=600&fit=crop&q=80", + "https://images.unsplash.com/photo-1552664730-d307ca884978?w=1200&h=600&fit=crop&q=80" + ], + image: "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?w=1200&h=600&fit=crop&q=80", + takeaways: [ + "thank you to all beta testers for valuable feedback", + "thousands of suggestions implemented", + "features built based on user needs", + "stronger platform thanks to community input" + ] + } +]; diff --git a/apps/web/src/app/(main)/dashboard/newsletters/page.tsx b/apps/web/src/app/(main)/dashboard/newsletters/page.tsx new file mode 100644 index 00000000..669abafb --- /dev/null +++ b/apps/web/src/app/(main)/dashboard/newsletters/page.tsx @@ -0,0 +1,264 @@ +"use client"; + +import { useState, useMemo } from "react"; +import Link from "next/link"; +import { GeistSans } from "geist/font/sans"; +import { Search, X, Calendar } from "lucide-react"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import Image from "next/image"; +import { newsletters } from "./data/newsletters"; +import type { Newsletter } from "@/types/newsletter"; + +const groupByMonth = (newslettersList: Newsletter[]) => { + const groups: { [key: string]: Newsletter[] } = {}; + newslettersList.forEach((newsletter) => { + const date = new Date(newsletter.date); + if (isNaN(date.getTime())) { + console.warn(`Invalid date for newsletter ${newsletter.id}: ${newsletter.date}`); + return; + } + const monthYear = date.toLocaleDateString("en-US", { + month: "long", + year: "numeric", + }); + if (!groups[monthYear]) { + groups[monthYear] = []; + } + groups[monthYear].push(newsletter); + }); + Object.keys(groups).forEach((key) => { + groups[key].sort( + (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime() + ); + }); + return groups; +}; + +const sortMonthKeys = (keys: string[]): string[] => { + return keys.sort((a, b) => { + const [monthA, yearA] = a.split(" "); + const [monthB, yearB] = b.split(" "); + const dateA = new Date(`${monthA} 1, ${yearA}`); + const dateB = new Date(`${monthB} 1, ${yearB}`); + return dateB.getTime() - dateA.getTime(); + }); +}; + +const getAvailableMonths = (newsletters: Newsletter[]): string[] => { + const months = newsletters + .map((n) => new Date(n.date)) + .filter((d) => !isNaN(d.getTime())) + .map((date) => + date.toLocaleDateString("en-US", { month: "long", year: "numeric" }) + ); + const uniqueMonths = Array.from(new Set(months)); + return sortMonthKeys(uniqueMonths); +}; + +const matchesSearchQuery = (newsletter: Newsletter, query: string): boolean => { + return ( + newsletter.title.toLowerCase().includes(query) || + newsletter.preview.toLowerCase().includes(query) || + newsletter.content.toLowerCase().includes(query) || + newsletter.author.toLowerCase().includes(query) || + newsletter.takeaways.some((takeaway) => takeaway.toLowerCase().includes(query)) + ); +}; + +const matchesMonthFilter = ( + newsletter: Newsletter, + selectedMonth: string +): boolean => { + if (selectedMonth === "all") return true; + const date = new Date(newsletter.date); + if (isNaN(date.getTime())) return false; + const monthYear = date.toLocaleDateString("en-US", { + month: "long", + year: "numeric", + }); + return monthYear === selectedMonth; +}; + +const filterNewsletters = ( + newsletters: Newsletter[], + searchQuery: string, + selectedMonth: string +): Newsletter[] => { + let filtered = newsletters; + if (searchQuery.trim()) { + const query = searchQuery.toLowerCase(); + filtered = filtered.filter((newsletter) => + matchesSearchQuery(newsletter, query) + ); + } + filtered = filtered.filter((newsletter) => + matchesMonthFilter(newsletter, selectedMonth) + ); + return filtered; +}; + +const NewsletterCard = ({ newsletter }: { newsletter: Newsletter }) => { + return ( + + + {newsletter.image && ( +
+ {newsletter.title} +
+ )} +
+

+ {newsletter.title} +

+
+
+ + {newsletter.date} +
+ by {newsletter.author} +
+

+ {newsletter.preview} +

+
+
+ + ); +}; + +export default function NewslettersPage() { + const [searchQuery, setSearchQuery] = useState(""); + const [selectedMonth, setSelectedMonth] = useState("all"); + + const availableMonths = useMemo( + () => getAvailableMonths(newsletters), + [] + ); + + const filteredNewsletters = useMemo( + () => filterNewsletters(newsletters, searchQuery, selectedMonth), + [searchQuery, selectedMonth] + ); + + const handleClearFilters = () => { + setSearchQuery(""); + setSelectedMonth("all"); + }; + + const hasActiveFilters = + searchQuery.trim() !== "" || selectedMonth !== "all"; + + const groupedNewsletters = groupByMonth(filteredNewsletters); + const sortedMonths = sortMonthKeys(Object.keys(groupedNewsletters)); + + return ( +
+
+
+

+ Newsletters +

+

+ Stay updated with the latest features, tips, and insights from + opensox.ai +

+
+ +
+
+
+ + setSearchQuery(e.target.value)} + className="pl-10 bg-card border-border" + /> +
+ +
+ + {hasActiveFilters && ( +
+ + {filteredNewsletters.length} result{filteredNewsletters.length !== 1 ? "s" : ""} + + +
+ )} +
+ + {filteredNewsletters.length === 0 ? ( +
+

+ {hasActiveFilters + ? "No newsletters match your filters" + : "No newsletters yet. Check back soon!"} +

+ {hasActiveFilters && ( + + )} +
+ ) : ( +
+ {sortedMonths.map((monthYear) => ( +
+

+ {monthYear} +

+
+ {groupedNewsletters[monthYear].map((newsletter) => ( + + ))} +
+
+ ))} +
+ )} +
+
+ ); +} diff --git a/apps/web/src/components/dashboard/Sidebar.tsx b/apps/web/src/components/dashboard/Sidebar.tsx index e8770831..5a70e8ca 100644 --- a/apps/web/src/components/dashboard/Sidebar.tsx +++ b/apps/web/src/components/dashboard/Sidebar.tsx @@ -14,14 +14,14 @@ import { ChevronDoubleRightIcon, SparklesIcon, StarIcon, - DocumentTextIcon, - Cog6ToothIcon, + HeartIcon, + EnvelopeIcon, + NewspaperIcon, } from "@heroicons/react/24/outline"; import { useShowSidebar } from "@/store/useShowSidebar"; import { signOut, useSession } from "next-auth/react"; import { ProfilePic } from "./ProfilePic"; -import { useSubscription } from "@/hooks/useSubscription"; -import { OpensoxProBadge } from "../sheet/OpensoxProBadge"; +import { useFilterStore } from "@/store/useFilterStore"; const SIDEBAR_ROUTES = [ { @@ -35,9 +35,9 @@ const SIDEBAR_ROUTES = [ icon: , }, { - path: "/dashboard/sheet", - label: "OSS Sheet", - icon: , + path: "/dashboard/newsletters", + label: "Newsletters", + icon: , }, ]; diff --git a/apps/web/src/components/ui/input.tsx b/apps/web/src/components/ui/input.tsx new file mode 100644 index 00000000..c64ef133 --- /dev/null +++ b/apps/web/src/components/ui/input.tsx @@ -0,0 +1,22 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +const Input = React.forwardRef>( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/apps/web/src/components/ui/select.tsx b/apps/web/src/components/ui/select.tsx new file mode 100644 index 00000000..817f3b99 --- /dev/null +++ b/apps/web/src/components/ui/select.tsx @@ -0,0 +1,160 @@ +"use client"; + +import * as React from "react"; +import * as SelectPrimitive from "@radix-ui/react-select"; +import { Check, ChevronDown, ChevronUp } from "lucide-react"; +import { cn } from "@/lib/utils"; + +const Select = SelectPrimitive.Root; + +const SelectGroup = SelectPrimitive.Group; + +const SelectValue = SelectPrimitive.Value; + +const SelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1", + className + )} + {...props} + > + {children} + + + + +)); +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName; + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => ( + + + + + {children} + + + + +)); +SelectContent.displayName = SelectPrimitive.Content.displayName; + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectLabel.displayName = SelectPrimitive.Label.displayName; + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + + {children} + +)); +SelectItem.displayName = SelectPrimitive.Item.displayName; + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectSeparator.displayName = SelectPrimitive.Separator.displayName; + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +}; + diff --git a/apps/web/src/types/index.ts b/apps/web/src/types/index.ts index 355bc959..9b1b21a5 100644 --- a/apps/web/src/types/index.ts +++ b/apps/web/src/types/index.ts @@ -1,2 +1,3 @@ export * from "./filter" export * from "./projects" +export * from "./newsletter" diff --git a/apps/web/src/types/newsletter.ts b/apps/web/src/types/newsletter.ts new file mode 100644 index 00000000..af1be347 --- /dev/null +++ b/apps/web/src/types/newsletter.ts @@ -0,0 +1,11 @@ +export interface Newsletter { + id: number; + title: string; + date: string; + author: string; + preview: string; + content: string; + image: string; + contentImages?: string[]; + takeaways: string[]; +}