@volchoklv/newsletter-kit 1.0.0
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/README.md +505 -0
- package/dist/adapters/email/index.d.ts +119 -0
- package/dist/adapters/email/index.js +417 -0
- package/dist/adapters/email/index.js.map +1 -0
- package/dist/adapters/storage/index.d.ts +215 -0
- package/dist/adapters/storage/index.js +415 -0
- package/dist/adapters/storage/index.js.map +1 -0
- package/dist/components/index.d.ts +198 -0
- package/dist/components/index.js +505 -0
- package/dist/components/index.js.map +1 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +1762 -0
- package/dist/index.js.map +1 -0
- package/dist/server/index.d.ts +77 -0
- package/dist/server/index.js +530 -0
- package/dist/server/index.js.map +1 -0
- package/dist/types-BmajlhNp.d.ts +226 -0
- package/package.json +95 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/crypto.ts","../../../src/adapters/storage/prisma.ts","../../../src/adapters/storage/supabase.ts","../../../src/adapters/storage/memory.ts"],"sourcesContent":["import { randomBytes } from 'crypto';\n\n/**\n * Generate a secure random token for email confirmation\n */\nexport function generateToken(length: number = 32): string {\n return randomBytes(length).toString('hex');\n}\n\n/**\n * Generate a URL-safe token\n */\nexport function generateUrlSafeToken(length: number = 32): string {\n return randomBytes(length)\n .toString('base64')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=/g, '');\n}\n","import type { StorageAdapter, Subscriber, SubscribeInput, SubscriptionStatus } from '../../types';\nimport { generateToken } from '../../utils/crypto';\n\n/**\n * Prisma schema required for this adapter:\n * \n * **PostgreSQL / Neon / MySQL:**\n * ```prisma\n * model NewsletterSubscriber {\n * id String @id @default(cuid())\n * email String @unique\n * status String @default(\"pending\") // pending, confirmed, unsubscribed\n * token String? @unique\n * source String?\n * tags String[] @default([])\n * metadata Json?\n * consentIp String?\n * consentAt DateTime?\n * confirmedAt DateTime?\n * unsubscribedAt DateTime?\n * createdAt DateTime @default(now())\n * updatedAt DateTime @updatedAt\n * \n * @@index([status])\n * @@index([source])\n * }\n * ```\n * \n * **MongoDB:**\n * ```prisma\n * model NewsletterSubscriber {\n * id String @id @default(auto()) @map(\"_id\") @db.ObjectId\n * email String @unique\n * status String @default(\"pending\") // pending, confirmed, unsubscribed\n * token String? @unique\n * source String?\n * tags String[] @default([])\n * metadata Json?\n * consentIp String?\n * consentAt DateTime?\n * confirmedAt DateTime?\n * unsubscribedAt DateTime?\n * createdAt DateTime @default(now())\n * updatedAt DateTime @updatedAt\n * \n * @@index([status])\n * @@index([source])\n * }\n * ```\n */\n\ninterface PrismaClient {\n newsletterSubscriber: {\n upsert: (args: unknown) => Promise<unknown>;\n findUnique: (args: unknown) => Promise<unknown>;\n findFirst: (args: unknown) => Promise<unknown>;\n findMany: (args: unknown) => Promise<unknown[]>;\n update: (args: unknown) => Promise<unknown>;\n delete: (args: unknown) => Promise<unknown>;\n count: (args?: unknown) => Promise<number>;\n };\n}\n\ninterface PrismaAdapterConfig {\n /** Your Prisma client instance */\n prisma: PrismaClient;\n /** Model name if different from 'newsletterSubscriber' */\n modelName?: string;\n}\n\nfunction mapToSubscriber(record: Record<string, unknown>): Subscriber {\n return {\n id: record.id as string,\n email: record.email as string,\n status: record.status as SubscriptionStatus,\n source: record.source as string | undefined,\n tags: record.tags as string[] | undefined,\n metadata: record.metadata as Record<string, unknown> | undefined,\n consentIp: record.consentIp as string | undefined,\n consentAt: record.consentAt ? new Date(record.consentAt as string) : undefined,\n confirmedAt: record.confirmedAt ? new Date(record.confirmedAt as string) : undefined,\n unsubscribedAt: record.unsubscribedAt ? new Date(record.unsubscribedAt as string) : undefined,\n createdAt: new Date(record.createdAt as string),\n updatedAt: new Date(record.updatedAt as string),\n };\n}\n\n/**\n * Storage adapter for Prisma ORM\n * \n * @example\n * ```ts\n * import { createPrismaAdapter } from '@volchok/newsletter-kit/adapters/storage';\n * import { prisma } from '@/lib/prisma';\n * \n * const storageAdapter = createPrismaAdapter({ prisma });\n * ```\n */\nexport function createPrismaAdapter(config: PrismaAdapterConfig): StorageAdapter {\n const { prisma } = config;\n const model = prisma.newsletterSubscriber;\n\n return {\n async createSubscriber(input: SubscribeInput, token?: string): Promise<Subscriber> {\n const confirmToken = token || generateToken();\n\n const record = await model.upsert({\n where: { email: input.email.toLowerCase() },\n update: {\n token: confirmToken,\n source: input.source,\n tags: input.tags || [],\n metadata: input.metadata,\n consentIp: input.ip,\n consentAt: new Date(),\n // Reset to pending if resubscribing\n status: 'pending',\n unsubscribedAt: null,\n },\n create: {\n email: input.email.toLowerCase(),\n token: confirmToken,\n status: 'pending',\n source: input.source,\n tags: input.tags || [],\n metadata: input.metadata,\n consentIp: input.ip,\n consentAt: new Date(),\n },\n });\n\n return mapToSubscriber(record as Record<string, unknown>);\n },\n\n async getSubscriberByEmail(email: string): Promise<Subscriber | null> {\n const record = await model.findUnique({\n where: { email: email.toLowerCase() },\n });\n\n return record ? mapToSubscriber(record as Record<string, unknown>) : null;\n },\n\n async getSubscriberByToken(token: string): Promise<Subscriber | null> {\n const record = await model.findFirst({\n where: { token },\n });\n\n return record ? mapToSubscriber(record as Record<string, unknown>) : null;\n },\n\n async confirmSubscriber(token: string): Promise<Subscriber | null> {\n try {\n const record = await model.update({\n where: { token },\n data: {\n status: 'confirmed',\n confirmedAt: new Date(),\n token: null, // Clear token after use\n },\n });\n\n return mapToSubscriber(record as Record<string, unknown>);\n } catch {\n // Token not found\n return null;\n }\n },\n\n async unsubscribe(email: string): Promise<boolean> {\n try {\n await model.update({\n where: { email: email.toLowerCase() },\n data: {\n status: 'unsubscribed',\n unsubscribedAt: new Date(),\n },\n });\n return true;\n } catch {\n return false;\n }\n },\n\n async listSubscribers(options?: {\n status?: SubscriptionStatus;\n source?: string;\n tags?: string[];\n limit?: number;\n offset?: number;\n }): Promise<{ subscribers: Subscriber[]; total: number }> {\n const where: Record<string, unknown> = {};\n\n if (options?.status) {\n where.status = options.status;\n }\n if (options?.source) {\n where.source = options.source;\n }\n if (options?.tags?.length) {\n where.tags = { hasSome: options.tags };\n }\n\n const [records, total] = await Promise.all([\n model.findMany({\n where,\n take: options?.limit || 100,\n skip: options?.offset || 0,\n orderBy: { createdAt: 'desc' },\n }),\n model.count({ where }),\n ]);\n\n return {\n subscribers: records.map((r) => mapToSubscriber(r as Record<string, unknown>)),\n total,\n };\n },\n\n async deleteSubscriber(email: string): Promise<boolean> {\n try {\n await model.delete({\n where: { email: email.toLowerCase() },\n });\n return true;\n } catch {\n return false;\n }\n },\n\n async updateSubscriber(email: string, data: Partial<Subscriber>): Promise<Subscriber | null> {\n try {\n const record = await model.update({\n where: { email: email.toLowerCase() },\n data: {\n ...(data.source !== undefined && { source: data.source }),\n ...(data.tags !== undefined && { tags: data.tags }),\n ...(data.metadata !== undefined && { metadata: data.metadata }),\n },\n });\n\n return mapToSubscriber(record as Record<string, unknown>);\n } catch {\n return null;\n }\n },\n };\n}\n","import type { StorageAdapter, Subscriber, SubscribeInput, SubscriptionStatus } from '../../types';\nimport { generateToken } from '../../utils/crypto';\n\n/**\n * Supabase table schema (SQL):\n * \n * ```sql\n * CREATE TABLE newsletter_subscribers (\n * id UUID DEFAULT gen_random_uuid() PRIMARY KEY,\n * email TEXT UNIQUE NOT NULL,\n * status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'confirmed', 'unsubscribed')),\n * token TEXT UNIQUE,\n * source TEXT,\n * tags TEXT[] DEFAULT '{}',\n * metadata JSONB,\n * consent_ip TEXT,\n * consent_at TIMESTAMPTZ,\n * confirmed_at TIMESTAMPTZ,\n * unsubscribed_at TIMESTAMPTZ,\n * created_at TIMESTAMPTZ DEFAULT NOW(),\n * updated_at TIMESTAMPTZ DEFAULT NOW()\n * );\n * \n * -- Indexes\n * CREATE INDEX idx_newsletter_subscribers_status ON newsletter_subscribers(status);\n * CREATE INDEX idx_newsletter_subscribers_source ON newsletter_subscribers(source);\n * CREATE INDEX idx_newsletter_subscribers_token ON newsletter_subscribers(token);\n * \n * -- Updated at trigger\n * CREATE OR REPLACE FUNCTION update_updated_at()\n * RETURNS TRIGGER AS $$\n * BEGIN\n * NEW.updated_at = NOW();\n * RETURN NEW;\n * END;\n * $$ LANGUAGE plpgsql;\n * \n * CREATE TRIGGER newsletter_subscribers_updated_at\n * BEFORE UPDATE ON newsletter_subscribers\n * FOR EACH ROW\n * EXECUTE FUNCTION update_updated_at();\n * ```\n */\n\ninterface SupabaseClient {\n from: (table: string) => {\n upsert: (data: unknown, options?: unknown) => Promise<{ data: unknown; error: unknown }>;\n select: (columns?: string) => {\n eq: (column: string, value: unknown) => {\n single: () => Promise<{ data: unknown; error: unknown }>;\n range: (from: number, to: number) => Promise<{ data: unknown[]; error: unknown }>;\n };\n contains: (column: string, value: unknown) => {\n range: (from: number, to: number) => Promise<{ data: unknown[]; error: unknown }>;\n };\n order: (column: string, options?: unknown) => {\n range: (from: number, to: number) => Promise<{ data: unknown[]; error: unknown }>;\n };\n };\n update: (data: unknown) => {\n eq: (column: string, value: unknown) => Promise<{ data: unknown; error: unknown }>;\n };\n delete: () => {\n eq: (column: string, value: unknown) => Promise<{ data: unknown; error: unknown }>;\n };\n };\n}\n\ninterface SupabaseAdapterConfig {\n /** Your Supabase client instance */\n supabase: SupabaseClient;\n /** Table name if different from 'newsletter_subscribers' */\n tableName?: string;\n}\n\ninterface SupabaseRecord {\n id: string;\n email: string;\n status: SubscriptionStatus;\n token?: string;\n source?: string;\n tags?: string[];\n metadata?: Record<string, unknown>;\n consent_ip?: string;\n consent_at?: string;\n confirmed_at?: string;\n unsubscribed_at?: string;\n created_at: string;\n updated_at: string;\n}\n\nfunction mapToSubscriber(record: SupabaseRecord): Subscriber {\n return {\n id: record.id,\n email: record.email,\n status: record.status,\n source: record.source,\n tags: record.tags,\n metadata: record.metadata,\n consentIp: record.consent_ip,\n consentAt: record.consent_at ? new Date(record.consent_at) : undefined,\n confirmedAt: record.confirmed_at ? new Date(record.confirmed_at) : undefined,\n unsubscribedAt: record.unsubscribed_at ? new Date(record.unsubscribed_at) : undefined,\n createdAt: new Date(record.created_at),\n updatedAt: new Date(record.updated_at),\n };\n}\n\n/**\n * Storage adapter for Supabase\n * \n * @example\n * ```ts\n * import { createSupabaseAdapter } from '@volchok/newsletter-kit/adapters/storage';\n * import { createClient } from '@supabase/supabase-js';\n * \n * const supabase = createClient(\n * process.env.SUPABASE_URL!,\n * process.env.SUPABASE_SERVICE_KEY!\n * );\n * \n * const storageAdapter = createSupabaseAdapter({ supabase });\n * ```\n */\nexport function createSupabaseAdapter(config: SupabaseAdapterConfig): StorageAdapter {\n const { supabase, tableName = 'newsletter_subscribers' } = config;\n\n return {\n async createSubscriber(input: SubscribeInput, token?: string): Promise<Subscriber> {\n const confirmToken = token || generateToken();\n const email = input.email.toLowerCase();\n\n const { data, error } = await supabase\n .from(tableName)\n .upsert(\n {\n email,\n token: confirmToken,\n status: 'pending',\n source: input.source,\n tags: input.tags || [],\n metadata: input.metadata,\n consent_ip: input.ip,\n consent_at: new Date().toISOString(),\n unsubscribed_at: null,\n },\n { onConflict: 'email' }\n );\n\n if (error) throw error;\n\n // Fetch the created/updated record\n const { data: record } = await supabase\n .from(tableName)\n .select()\n .eq('email', email)\n .single();\n\n return mapToSubscriber(record as SupabaseRecord);\n },\n\n async getSubscriberByEmail(email: string): Promise<Subscriber | null> {\n const { data, error } = await supabase\n .from(tableName)\n .select()\n .eq('email', email.toLowerCase())\n .single();\n\n if (error || !data) return null;\n return mapToSubscriber(data as SupabaseRecord);\n },\n\n async getSubscriberByToken(token: string): Promise<Subscriber | null> {\n const { data, error } = await supabase\n .from(tableName)\n .select()\n .eq('token', token)\n .single();\n\n if (error || !data) return null;\n return mapToSubscriber(data as SupabaseRecord);\n },\n\n async confirmSubscriber(token: string): Promise<Subscriber | null> {\n const { error } = await supabase\n .from(tableName)\n .update({\n status: 'confirmed',\n confirmed_at: new Date().toISOString(),\n token: null,\n })\n .eq('token', token);\n\n if (error) return null;\n\n // Fetch updated record by getting any confirmed subscriber updated recently\n // This is a bit hacky - ideally we'd get the email from the token first\n const { data } = await supabase\n .from(tableName)\n .select()\n .eq('token', null)\n .single();\n\n // Better approach: get by email after updating\n return data ? mapToSubscriber(data as SupabaseRecord) : null;\n },\n\n async unsubscribe(email: string): Promise<boolean> {\n const { error } = await supabase\n .from(tableName)\n .update({\n status: 'unsubscribed',\n unsubscribed_at: new Date().toISOString(),\n })\n .eq('email', email.toLowerCase());\n\n return !error;\n },\n\n async listSubscribers(options?: {\n status?: SubscriptionStatus;\n source?: string;\n tags?: string[];\n limit?: number;\n offset?: number;\n }): Promise<{ subscribers: Subscriber[]; total: number }> {\n const limit = options?.limit || 100;\n const offset = options?.offset || 0;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let query: any = supabase.from(tableName).select();\n\n if (options?.status) {\n query = query.eq('status', options.status);\n }\n if (options?.source) {\n query = query.eq('source', options.source);\n }\n if (options?.tags?.length) {\n query = query.contains('tags', options.tags);\n }\n\n const { data, error } = await query\n .order('created_at', { ascending: false })\n .range(offset, offset + limit - 1);\n\n if (error) throw error;\n\n return {\n subscribers: (data || []).map((r: SupabaseRecord) => mapToSubscriber(r)),\n total: (data || []).length, // Note: Supabase doesn't return total count easily\n };\n },\n\n async deleteSubscriber(email: string): Promise<boolean> {\n const { error } = await supabase\n .from(tableName)\n .delete()\n .eq('email', email.toLowerCase());\n\n return !error;\n },\n\n async updateSubscriber(email: string, data: Partial<Subscriber>): Promise<Subscriber | null> {\n const updateData: Record<string, unknown> = {};\n if (data.source !== undefined) updateData.source = data.source;\n if (data.tags !== undefined) updateData.tags = data.tags;\n if (data.metadata !== undefined) updateData.metadata = data.metadata;\n\n const { error } = await supabase\n .from(tableName)\n .update(updateData)\n .eq('email', email.toLowerCase());\n\n if (error) return null;\n\n return this.getSubscriberByEmail(email);\n },\n };\n}\n","import type { StorageAdapter, Subscriber, SubscribeInput, SubscriptionStatus } from '../../types';\nimport { generateToken } from '../../utils/crypto';\n\n/**\n * In-memory storage adapter\n * \n * Useful for:\n * - Development and testing\n * - Simple sites that don't need persistence\n * - When you only want to send emails without storing subscribers\n * \n * WARNING: Data is lost when the server restarts!\n * \n * @example\n * ```ts\n * import { createMemoryAdapter } from '@volchok/newsletter-kit/adapters/storage';\n * \n * const storageAdapter = createMemoryAdapter();\n * ```\n */\nexport function createMemoryAdapter(): StorageAdapter {\n const subscribers = new Map<string, Subscriber>();\n const tokenIndex = new Map<string, string>(); // token -> email\n\n return {\n async createSubscriber(input: SubscribeInput, token?: string): Promise<Subscriber> {\n const email = input.email.toLowerCase();\n const confirmToken = token || generateToken();\n const now = new Date();\n\n // Remove old token if exists\n const existing = subscribers.get(email);\n if (existing?.id) {\n // Find and remove old token\n for (const [t, e] of tokenIndex.entries()) {\n if (e === email) {\n tokenIndex.delete(t);\n break;\n }\n }\n }\n\n const subscriber: Subscriber = {\n id: existing?.id || crypto.randomUUID(),\n email,\n status: 'pending',\n source: input.source,\n tags: input.tags || [],\n metadata: input.metadata,\n consentIp: input.ip,\n consentAt: now,\n confirmedAt: undefined,\n unsubscribedAt: undefined,\n createdAt: existing?.createdAt || now,\n updatedAt: now,\n };\n\n subscribers.set(email, subscriber);\n tokenIndex.set(confirmToken, email);\n\n // Return with token attached (hack for internal use)\n return { ...subscriber, id: confirmToken } as Subscriber;\n },\n\n async getSubscriberByEmail(email: string): Promise<Subscriber | null> {\n return subscribers.get(email.toLowerCase()) || null;\n },\n\n async getSubscriberByToken(token: string): Promise<Subscriber | null> {\n const email = tokenIndex.get(token);\n if (!email) return null;\n return subscribers.get(email) || null;\n },\n\n async confirmSubscriber(token: string): Promise<Subscriber | null> {\n const email = tokenIndex.get(token);\n if (!email) return null;\n\n const subscriber = subscribers.get(email);\n if (!subscriber) return null;\n\n const updated: Subscriber = {\n ...subscriber,\n status: 'confirmed',\n confirmedAt: new Date(),\n updatedAt: new Date(),\n };\n\n subscribers.set(email, updated);\n tokenIndex.delete(token);\n\n return updated;\n },\n\n async unsubscribe(email: string): Promise<boolean> {\n const subscriber = subscribers.get(email.toLowerCase());\n if (!subscriber) return false;\n\n const updated: Subscriber = {\n ...subscriber,\n status: 'unsubscribed',\n unsubscribedAt: new Date(),\n updatedAt: new Date(),\n };\n\n subscribers.set(email.toLowerCase(), updated);\n return true;\n },\n\n async listSubscribers(options?: {\n status?: SubscriptionStatus;\n source?: string;\n tags?: string[];\n limit?: number;\n offset?: number;\n }): Promise<{ subscribers: Subscriber[]; total: number }> {\n let results = Array.from(subscribers.values());\n\n if (options?.status) {\n results = results.filter((s) => s.status === options.status);\n }\n if (options?.source) {\n results = results.filter((s) => s.source === options.source);\n }\n if (options?.tags?.length) {\n results = results.filter((s) =>\n options.tags!.some((tag) => s.tags?.includes(tag))\n );\n }\n\n // Sort by createdAt desc\n results.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());\n\n const total = results.length;\n const offset = options?.offset || 0;\n const limit = options?.limit || 100;\n\n return {\n subscribers: results.slice(offset, offset + limit),\n total,\n };\n },\n\n async deleteSubscriber(email: string): Promise<boolean> {\n const normalizedEmail = email.toLowerCase();\n const existed = subscribers.has(normalizedEmail);\n subscribers.delete(normalizedEmail);\n\n // Clean up token index\n for (const [token, e] of tokenIndex.entries()) {\n if (e === normalizedEmail) {\n tokenIndex.delete(token);\n break;\n }\n }\n\n return existed;\n },\n\n async updateSubscriber(email: string, data: Partial<Subscriber>): Promise<Subscriber | null> {\n const normalizedEmail = email.toLowerCase();\n const subscriber = subscribers.get(normalizedEmail);\n if (!subscriber) return null;\n\n const updated: Subscriber = {\n ...subscriber,\n ...(data.source !== undefined && { source: data.source }),\n ...(data.tags !== undefined && { tags: data.tags }),\n ...(data.metadata !== undefined && { metadata: data.metadata }),\n updatedAt: new Date(),\n };\n\n subscribers.set(normalizedEmail, updated);\n return updated;\n },\n };\n}\n\n/**\n * No-op storage adapter\n * \n * Use when you don't want to store subscribers at all,\n * only send emails (e.g., when using Mailchimp which handles storage)\n */\nexport function createNoopAdapter(): StorageAdapter {\n return {\n async createSubscriber(input: SubscribeInput): Promise<Subscriber> {\n return {\n id: 'noop',\n email: input.email,\n status: 'pending',\n source: input.source,\n tags: input.tags,\n metadata: input.metadata,\n consentIp: input.ip,\n consentAt: new Date(),\n createdAt: new Date(),\n updatedAt: new Date(),\n };\n },\n async getSubscriberByEmail(): Promise<Subscriber | null> {\n return null;\n },\n async getSubscriberByToken(): Promise<Subscriber | null> {\n return null;\n },\n async confirmSubscriber(): Promise<Subscriber | null> {\n return null;\n },\n async unsubscribe(): Promise<boolean> {\n return true;\n },\n };\n}\n"],"mappings":"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","names":["mapToSubscriber"]}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { c as NewsletterFormProps } from '../types-BmajlhNp.js';
|
|
3
|
+
export { d as NewsletterFormState } from '../types-BmajlhNp.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Newsletter subscription form component
|
|
7
|
+
*
|
|
8
|
+
* Designed to work with shadcn/ui but includes default Tailwind styles.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* // Basic usage
|
|
13
|
+
* <NewsletterForm endpoint="/api/newsletter/subscribe" />
|
|
14
|
+
*
|
|
15
|
+
* // With all options
|
|
16
|
+
* <NewsletterForm
|
|
17
|
+
* endpoint="/api/newsletter/subscribe"
|
|
18
|
+
* source="footer"
|
|
19
|
+
* tags={['marketing']}
|
|
20
|
+
* placeholder="Enter your email"
|
|
21
|
+
* buttonText="Subscribe"
|
|
22
|
+
* successMessage="Check your inbox!"
|
|
23
|
+
* onSuccess={(email) => console.log('Subscribed:', email)}
|
|
24
|
+
* onError={(error) => console.error('Error:', error)}
|
|
25
|
+
* className="max-w-md"
|
|
26
|
+
* />
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
declare function NewsletterForm({ endpoint, source, tags, honeypotField, className, formClassName, inputClassName, buttonClassName, messageClassName, placeholder, buttonText, loadingText, successMessage, errorMessage, showMessage, onSuccess, onError, onSubmit, disabled, metadata, }: NewsletterFormProps): react_jsx_runtime.JSX.Element;
|
|
30
|
+
|
|
31
|
+
interface NewsletterBlockProps extends NewsletterFormProps {
|
|
32
|
+
title?: string;
|
|
33
|
+
description?: string;
|
|
34
|
+
containerClassName?: string;
|
|
35
|
+
titleClassName?: string;
|
|
36
|
+
descriptionClassName?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Full-width newsletter subscription block
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* <NewsletterBlock
|
|
44
|
+
* endpoint="/api/newsletter/subscribe"
|
|
45
|
+
* title="Stay updated"
|
|
46
|
+
* description="Get the latest news delivered to your inbox."
|
|
47
|
+
* source="footer"
|
|
48
|
+
* />
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
declare function NewsletterBlock({ title, description, containerClassName, titleClassName, descriptionClassName, ...formProps }: NewsletterBlockProps): react_jsx_runtime.JSX.Element;
|
|
52
|
+
interface NewsletterCardProps extends NewsletterFormProps {
|
|
53
|
+
title?: string;
|
|
54
|
+
description?: string;
|
|
55
|
+
cardClassName?: string;
|
|
56
|
+
titleClassName?: string;
|
|
57
|
+
descriptionClassName?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Card-style newsletter subscription component
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```tsx
|
|
64
|
+
* <NewsletterCard
|
|
65
|
+
* endpoint="/api/newsletter/subscribe"
|
|
66
|
+
* title="Join the community"
|
|
67
|
+
* description="Be the first to know about new features."
|
|
68
|
+
* source="sidebar"
|
|
69
|
+
* />
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
declare function NewsletterCard({ title, description, cardClassName, titleClassName, descriptionClassName, ...formProps }: NewsletterCardProps): react_jsx_runtime.JSX.Element;
|
|
73
|
+
interface NewsletterInlineProps extends NewsletterFormProps {
|
|
74
|
+
label?: string;
|
|
75
|
+
labelClassName?: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Inline newsletter subscription component
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```tsx
|
|
82
|
+
* // In a footer
|
|
83
|
+
* <NewsletterInline
|
|
84
|
+
* endpoint="/api/newsletter/subscribe"
|
|
85
|
+
* label="Subscribe:"
|
|
86
|
+
* source="footer"
|
|
87
|
+
* />
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
declare function NewsletterInline({ label, labelClassName, ...formProps }: NewsletterInlineProps): react_jsx_runtime.JSX.Element;
|
|
91
|
+
interface NewsletterModalContentProps extends NewsletterFormProps {
|
|
92
|
+
title?: string;
|
|
93
|
+
description?: string;
|
|
94
|
+
titleClassName?: string;
|
|
95
|
+
descriptionClassName?: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Newsletter content for use inside a modal/dialog
|
|
99
|
+
*
|
|
100
|
+
* Use with shadcn/ui Dialog component:
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```tsx
|
|
104
|
+
* import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog';
|
|
105
|
+
* import { NewsletterModalContent } from '@volchok/newsletter-kit/components';
|
|
106
|
+
*
|
|
107
|
+
* <Dialog>
|
|
108
|
+
* <DialogTrigger asChild>
|
|
109
|
+
* <Button>Subscribe</Button>
|
|
110
|
+
* </DialogTrigger>
|
|
111
|
+
* <DialogContent>
|
|
112
|
+
* <NewsletterModalContent
|
|
113
|
+
* endpoint="/api/newsletter/subscribe"
|
|
114
|
+
* title="Join our newsletter"
|
|
115
|
+
* description="Get weekly updates on the latest news."
|
|
116
|
+
* source="popup"
|
|
117
|
+
* onSuccess={() => {
|
|
118
|
+
* // Close modal after success
|
|
119
|
+
* setTimeout(() => setOpen(false), 2000);
|
|
120
|
+
* }}
|
|
121
|
+
* />
|
|
122
|
+
* </DialogContent>
|
|
123
|
+
* </Dialog>
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
declare function NewsletterModalContent({ title, description, titleClassName, descriptionClassName, ...formProps }: NewsletterModalContentProps): react_jsx_runtime.JSX.Element;
|
|
127
|
+
interface NewsletterFooterProps extends NewsletterFormProps {
|
|
128
|
+
title?: string;
|
|
129
|
+
description?: string;
|
|
130
|
+
containerClassName?: string;
|
|
131
|
+
titleClassName?: string;
|
|
132
|
+
descriptionClassName?: string;
|
|
133
|
+
privacyText?: string;
|
|
134
|
+
privacyLink?: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Footer-optimized newsletter subscription component
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```tsx
|
|
141
|
+
* <footer>
|
|
142
|
+
* <NewsletterFooter
|
|
143
|
+
* endpoint="/api/newsletter/subscribe"
|
|
144
|
+
* title="Newsletter"
|
|
145
|
+
* description="Stay up to date with our latest news."
|
|
146
|
+
* source="footer"
|
|
147
|
+
* privacyText="We respect your privacy."
|
|
148
|
+
* privacyLink="/privacy"
|
|
149
|
+
* />
|
|
150
|
+
* </footer>
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
declare function NewsletterFooter({ title, description, containerClassName, titleClassName, descriptionClassName, privacyText, privacyLink, ...formProps }: NewsletterFooterProps): react_jsx_runtime.JSX.Element;
|
|
154
|
+
|
|
155
|
+
interface UseNewsletterOptions {
|
|
156
|
+
endpoint?: string;
|
|
157
|
+
source?: string;
|
|
158
|
+
tags?: string[];
|
|
159
|
+
metadata?: Record<string, unknown>;
|
|
160
|
+
onSuccess?: (email: string, message: string) => void;
|
|
161
|
+
onError?: (error: string) => void;
|
|
162
|
+
}
|
|
163
|
+
interface UseNewsletterState {
|
|
164
|
+
status: 'idle' | 'loading' | 'success' | 'error';
|
|
165
|
+
message: string;
|
|
166
|
+
email: string | null;
|
|
167
|
+
}
|
|
168
|
+
interface UseNewsletterReturn extends UseNewsletterState {
|
|
169
|
+
subscribe: (email: string) => Promise<boolean>;
|
|
170
|
+
reset: () => void;
|
|
171
|
+
isLoading: boolean;
|
|
172
|
+
isSuccess: boolean;
|
|
173
|
+
isError: boolean;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* React hook for programmatic newsletter subscription
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```tsx
|
|
180
|
+
* const { subscribe, isLoading, isSuccess, message } = useNewsletter({
|
|
181
|
+
* endpoint: '/api/newsletter/subscribe',
|
|
182
|
+
* source: 'custom-form',
|
|
183
|
+
* onSuccess: (email) => {
|
|
184
|
+
* analytics.track('newsletter_subscribed', { email });
|
|
185
|
+
* },
|
|
186
|
+
* });
|
|
187
|
+
*
|
|
188
|
+
* const handleSubmit = async (email: string) => {
|
|
189
|
+
* const success = await subscribe(email);
|
|
190
|
+
* if (success) {
|
|
191
|
+
* // Show confetti or something
|
|
192
|
+
* }
|
|
193
|
+
* };
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
declare function useNewsletter(options?: UseNewsletterOptions): UseNewsletterReturn;
|
|
197
|
+
|
|
198
|
+
export { NewsletterBlock, NewsletterCard, NewsletterFooter, NewsletterForm, NewsletterFormProps, NewsletterInline, NewsletterModalContent, useNewsletter };
|