From 13d15883b12eda0f5a881c6f7055dcdc4aa6d6be Mon Sep 17 00:00:00 2001 From: Nora Date: Fri, 26 Jun 2026 05:25:36 -0600 Subject: [PATCH] =?UTF-8?q?fix:=20react-doctor=20unused-export=20141?= =?UTF-8?q?=E2=86=92~80=20(remove=20dead=20exports,=20enums/marketing=20un?= =?UTF-8?q?used,=20inline=20campaignStatusEnum)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/schema/enums.ts | 75 ------------------- db/schema/marketing.ts | 9 ++- src/actions/admin/password.ts | 2 +- src/actions/billing/stripe-checkout.ts | 2 +- src/actions/communications/contacts.ts | 4 +- src/actions/dashboard.ts | 2 +- .../email-automation/abandoned-cart.ts | 2 +- .../email-automation/welcome-sequence.ts | 2 +- src/actions/harvest-reach/campaigns.ts | 2 +- src/actions/integrations/ai-providers.ts | 6 +- src/actions/offline-dispatcher.ts | 2 +- src/actions/orders.ts | 8 +- src/actions/square-sync-ui.ts | 2 +- src/actions/storefront.ts | 14 ++-- src/actions/time-tracking/index.ts | 4 +- src/actions/water-log/admin.ts | 2 +- src/actions/water-log/field.ts | 6 +- src/actions/wholesale-register.ts | 2 +- src/auth.config.ts | 2 +- src/lib/ai-provider-models.ts | 4 +- src/lib/analytics.ts | 6 +- src/lib/brand-scope.ts | 2 +- src/lib/column-detector.ts | 4 +- src/lib/date-utils.ts | 12 +-- src/lib/feature-flags.ts | 4 +- src/lib/format-date.ts | 2 +- src/lib/pricing.ts | 20 ++--- src/lib/pwa.ts | 6 +- src/lib/rate-limit.ts | 6 +- src/lib/sentry.ts | 6 +- src/lib/server-log.ts | 2 +- src/lib/storage.ts | 10 +-- src/lib/stripe-billing.ts | 46 ++++++------ src/lib/water-log-reporting.ts | 2 +- 34 files changed, 106 insertions(+), 174 deletions(-) delete mode 100644 db/schema/enums.ts diff --git a/db/schema/enums.ts b/db/schema/enums.ts deleted file mode 100644 index 7bc7f9c..0000000 --- a/db/schema/enums.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Shared enums for the SaaS schema. Mirrored in SQL as TEXT + CHECK. - * - * Usage: - * import { tenantStatusEnum, type TenantStatus } from "@/db/schema/enums"; - * import { pgEnum } from "drizzle-orm/pg-core"; - * - * export const tenantStatus = pgEnum("tenant_status", tenantStatusEnum); - */ - -export const tenantStatusEnum = [ - "trial", - "active", - "past_due", - "suspended", - "churned", -] as const; -export type TenantStatus = (typeof tenantStatusEnum)[number]; - -export const authProviderEnum = ["dev", "google", "email"] as const; -export type AuthProvider = (typeof authProviderEnum)[number]; - -export const roleEnum = ["platform_admin", "brand_admin", "store_employee"] as const; -export type Role = (typeof roleEnum)[number]; - -export const planCodeEnum = ["starter", "farm", "enterprise"] as const; -export type PlanCode = (typeof planCodeEnum)[number]; - -export const addOnCodeEnum = [ - "wholesale_portal", - "harvest_reach", - "ai_tools", - "water_log", - "square_sync", - "sms_campaigns", -] as const; -export type AddOnCode = (typeof addOnCodeEnum)[number]; - -export const subscriptionStatusEnum = [ - "trialing", - "active", - "past_due", - "canceled", - "incomplete", -] as const; -export type SubscriptionStatus = (typeof subscriptionStatusEnum)[number]; - -export const addOnStatusEnum = ["active", "canceled"] as const; -export type AddOnStatus = (typeof addOnStatusEnum)[number]; - -export const stopStatusEnum = ["active", "paused", "closed"] as const; -export type StopStatus = (typeof stopStatusEnum)[number]; - -export const orderStatusEnum = [ - "pending", - "confirmed", - "fulfilled", - "canceled", -] as const; -export type OrderStatus = (typeof orderStatusEnum)[number]; - -export const fulfillmentEnum = ["pickup", "ship", "mixed"] as const; -export type Fulfillment = (typeof fulfillmentEnum)[number]; - -export const itemFulfillmentEnum = ["pickup", "ship"] as const; -export type ItemFulfillment = (typeof itemFulfillmentEnum)[number]; - -export const campaignStatusEnum = [ - "draft", - "scheduled", - "sending", - "sent", - "canceled", -] as const; -export type CampaignStatus = (typeof campaignStatusEnum)[number]; diff --git a/db/schema/marketing.ts b/db/schema/marketing.ts index 2758010..0bd71c6 100644 --- a/db/schema/marketing.ts +++ b/db/schema/marketing.ts @@ -10,9 +10,16 @@ import { timestamp, index, } from "drizzle-orm/pg-core"; -import { campaignStatusEnum } from "./enums"; import { brands } from "./brands"; +const campaignStatusEnum = [ + "draft", + "scheduled", + "sending", + "sent", + "canceled", +] as const; + export const emailTemplates = pgTable( "email_templates", { diff --git a/src/actions/admin/password.ts b/src/actions/admin/password.ts index eaa3a9c..7ea10e9 100644 --- a/src/actions/admin/password.ts +++ b/src/actions/admin/password.ts @@ -14,7 +14,7 @@ const MIN_PASSWORD_LENGTH = 8; * Use this for admin-initiated password resets. For self-service * password changes, users should use the forgot password flow. */ -export async function updatePasswordAction( +async function updatePasswordAction( userId: string, newPassword: string ): Promise<{ success: boolean; error?: string }> { diff --git a/src/actions/billing/stripe-checkout.ts b/src/actions/billing/stripe-checkout.ts index f6d3be4..8494dc0 100644 --- a/src/actions/billing/stripe-checkout.ts +++ b/src/actions/billing/stripe-checkout.ts @@ -28,7 +28,7 @@ function getPriceId(key: string): string | null { // ── Checkout session creation ───────────────────────────────────────────────── -export async function createStripeCheckoutSession( +async function createStripeCheckoutSession( brandId: string, priceKey: string, successPath: string, diff --git a/src/actions/communications/contacts.ts b/src/actions/communications/contacts.ts index 7d36c98..e28deae 100644 --- a/src/actions/communications/contacts.ts +++ b/src/actions/communications/contacts.ts @@ -196,7 +196,7 @@ export type UpsertContactResult = { error: string; }; -export async function upsertContact(contact: { +async function upsertContact(contact: { id?: string; brand_id: string; email?: string; @@ -384,7 +384,7 @@ export type OptOutResult = { error: string; }; -export async function optOutContact(params: { +async function optOutContact(params: { email: string; brandId: string; method: "email" | "sms"; diff --git a/src/actions/dashboard.ts b/src/actions/dashboard.ts index 9788718..8d4b21d 100644 --- a/src/actions/dashboard.ts +++ b/src/actions/dashboard.ts @@ -237,7 +237,7 @@ await getSession(); } } -export async function getDashboardSummary(): Promise { +async function getDashboardSummary(): Promise { await getSession(); try { diff --git a/src/actions/email-automation/abandoned-cart.ts b/src/actions/email-automation/abandoned-cart.ts index aba61c6..7ce13d7 100644 --- a/src/actions/email-automation/abandoned-cart.ts +++ b/src/actions/email-automation/abandoned-cart.ts @@ -251,7 +251,7 @@ await getSession(); // ── Mark cart as recovered when order is placed ──────────────────────────────── -export async function markCartRecovered(cartId: string, orderId: string): Promise { +async function markCartRecovered(cartId: string, orderId: string): Promise { await getSession(); void cartId; void orderId; diff --git a/src/actions/email-automation/welcome-sequence.ts b/src/actions/email-automation/welcome-sequence.ts index 2b54bee..c40f46f 100644 --- a/src/actions/email-automation/welcome-sequence.ts +++ b/src/actions/email-automation/welcome-sequence.ts @@ -181,7 +181,7 @@ function buildWelcomeEmail(params: { // ── Send one welcome email ───────────────────────────────────────────────────── -export async function sendWelcomeEmail( +async function sendWelcomeEmail( entry: WelcomeSequenceEntry, step: number ): Promise<{ success: boolean; error?: string }> { diff --git a/src/actions/harvest-reach/campaigns.ts b/src/actions/harvest-reach/campaigns.ts index 2806bfb..22005b6 100644 --- a/src/actions/harvest-reach/campaigns.ts +++ b/src/actions/harvest-reach/campaigns.ts @@ -75,7 +75,7 @@ function rowToCampaign(row: typeof campaigns.$inferSelect): Campaign { }; } -export async function getHarvestReachCampaigns( +async function getHarvestReachCampaigns( brandId: string ): Promise<{ success: true; campaigns: Campaign[] } | { success: false; error: string }> { diff --git a/src/actions/integrations/ai-providers.ts b/src/actions/integrations/ai-providers.ts index a855b7a..d6f1497 100644 --- a/src/actions/integrations/ai-providers.ts +++ b/src/actions/integrations/ai-providers.ts @@ -196,7 +196,7 @@ await getSession(); const settings = await getAIProviderSettings(brandId); // ── Custom integrations ───────────────────────────────────────────────────────── -export async function getCustomIntegrations(brandId: string): Promise { +async function getCustomIntegrations(brandId: string): Promise { await getSession(); try { const res = await pool.query( @@ -209,7 +209,7 @@ await getSession(); try { } } -export async function upsertCustomIntegration( +async function upsertCustomIntegration( brandId: string, integration: CustomIntegration ): Promise<{ success: boolean; integrations?: CustomIntegration[]; error?: string }> { @@ -230,7 +230,7 @@ await getSession(); const adminUser = await getAdminUser(); } } -export async function deleteCustomIntegration( +async function deleteCustomIntegration( brandId: string, integrationId: string ): Promise<{ success: boolean; error?: string }> { diff --git a/src/actions/offline-dispatcher.ts b/src/actions/offline-dispatcher.ts index 4144ceb..8b3b9ea 100644 --- a/src/actions/offline-dispatcher.ts +++ b/src/actions/offline-dispatcher.ts @@ -46,7 +46,7 @@ interface ClientAction { }>; } -export async function listClientActions(): Promise { +async function listClientActions(): Promise { await getSession(); // Wire real server actions here as they land: // { name: "markOrderReady", handler: markOrderReady }, diff --git a/src/actions/orders.ts b/src/actions/orders.ts index cc45f03..843067f 100644 --- a/src/actions/orders.ts +++ b/src/actions/orders.ts @@ -136,21 +136,21 @@ await getSession(); const adminUser = await getAdminUser(); } } -export async function getAdminStops(): Promise { +async function getAdminStops(): Promise { await getSession(); const result = await getAdminOrders(); if (!result.success) return []; return result.stops; } -export async function getAdminPendingOrders(): Promise { +async function getAdminPendingOrders(): Promise { await getSession(); const result = await getAdminOrders(); if (!result.success) return []; return result.orders.filter((o) => !o.pickup_complete); } -export async function getAdminPickedUpOrders(): Promise { +async function getAdminPickedUpOrders(): Promise { await getSession(); const result = await getAdminOrders(); if (!result.success) return []; @@ -181,7 +181,7 @@ await getSession(); const adminUser = await getAdminUser(); * When the pickup flow is rebuilt against the SaaS schema, this should * be replaced by a Drizzle update on a `pickup_events` table. */ -export async function toggleOrderPickupComplete(params: { +async function toggleOrderPickupComplete(params: { orderId: string; pickupComplete: boolean; }): Promise<{ success: true } | { success: false; error: string }> { diff --git a/src/actions/square-sync-ui.ts b/src/actions/square-sync-ui.ts index 5d6b6cd..41d4685 100644 --- a/src/actions/square-sync-ui.ts +++ b/src/actions/square-sync-ui.ts @@ -87,7 +87,7 @@ await getSession(); const adminUser = await getAdminUser(); * sync log query. Returns 0 when the caller isn't authorized — never * throws, so callers can render "0" while the auth check settles. */ -export async function getSquareQueueCount(brandId: string): Promise { +async function getSquareQueueCount(brandId: string): Promise { await getSession(); const adminUser = await getAdminUser(); if (!adminUser) return 0; diff --git a/src/actions/storefront.ts b/src/actions/storefront.ts index 2bfb2d9..121ce5a 100644 --- a/src/actions/storefront.ts +++ b/src/actions/storefront.ts @@ -58,7 +58,7 @@ export type StorefrontData = { * Look up a brand by its public slug. Returns `null` if no brand matches * (which means the storefront slug is wrong, or the brand was renamed). */ -export async function getStorefrontBrandBySlug( +async function getStorefrontBrandBySlug( slug: string, ): Promise { @@ -76,7 +76,7 @@ await getSession(); const { rows } = await pool.query( * Public stops for a brand storefront. Filters out draft / private stops * and any stop whose date has already passed. */ -export async function getStorefrontStops(brandId: string): Promise { +async function getStorefrontStops(brandId: string): Promise { await getSession(); const { rows } = await pool.query( `SELECT id, brand_id, name, city, state, zip, address, location, @@ -101,7 +101,7 @@ await getSession(); const { rows } = await pool.query( * float in **dollars** so callers can format directly as `$${price}` * without doing the cents-to-dollars math. */ -export async function getStorefrontProducts( +async function getStorefrontProducts( brandId: string, ): Promise { @@ -127,7 +127,7 @@ await getSession(); const { rows } = await pool.query( * Returns `{ brand: null, stops: [], products: [] }` if the slug doesn't * match any brand — pages fall back to default copy in that case. */ -export async function getStorefrontData(slug: string): Promise { +async function getStorefrontData(slug: string): Promise { await getSession(); const brand = await getStorefrontBrandBySlug(slug); if (!brand) return { brand: null, stops: [], products: [] }; @@ -151,7 +151,7 @@ export type StorefrontWholesaleSettings = { * fields don't exist on `wholesale_settings` yet and shouldn't be * surfaced here. If you need them, add a migration first. */ -export async function getStorefrontWholesaleSettings( +async function getStorefrontWholesaleSettings( slug: string, ): Promise { @@ -178,7 +178,7 @@ export type WholesalePortalConfig = { * to show the online-payment button. Doesn't require auth (the portal * already validated the session before this is called). */ -export async function getWholesalePortalConfig( +async function getWholesalePortalConfig( brandId: string, ): Promise { @@ -199,7 +199,7 @@ export type BrandName = { name: string }; * portal header to render the brand name without leaking any other * brand columns (logo, plan, stripe keys, etc.). */ -export async function getStorefrontBrandName( +async function getStorefrontBrandName( brandId: string, ): Promise { diff --git a/src/actions/time-tracking/index.ts b/src/actions/time-tracking/index.ts index 9907e90..14c32a4 100644 --- a/src/actions/time-tracking/index.ts +++ b/src/actions/time-tracking/index.ts @@ -167,7 +167,7 @@ export async function getWorkerTimeLogs( await getSession(); return []; } -export async function updateWorkerTimeLog( +async function updateWorkerTimeLog( _logId: string, _taskName: string, _clockIn: string, @@ -181,7 +181,7 @@ await getSession(); const adminUser = await getAdminUser(); return { success: false, error: "Time tracking is not configured" }; } -export async function deleteWorkerTimeLog(_logId: string): Promise<{ success: boolean; error?: string }> { +async function deleteWorkerTimeLog(_logId: string): Promise<{ success: boolean; error?: string }> { await getSession(); const adminUser = await getAdminUser(); if (!adminUser) return { success: false, error: "Not authenticated" }; diff --git a/src/actions/water-log/admin.ts b/src/actions/water-log/admin.ts index 1dce77d..8b83f22 100644 --- a/src/actions/water-log/admin.ts +++ b/src/actions/water-log/admin.ts @@ -125,7 +125,7 @@ async function requireWaterAdminPermission() { * For the `/water/admin` (PIN) portal. Returns the admin + brand if a * valid session cookie is present. Falls back to a 401-shaped error. */ -export async function requireWaterAdminSession(): Promise< +async function requireWaterAdminSession(): Promise< | { ok: true; adminUserId: string; brandId: string } | { ok: false; error: string } > { diff --git a/src/actions/water-log/field.ts b/src/actions/water-log/field.ts index 012a6df..67c707c 100644 --- a/src/actions/water-log/field.ts +++ b/src/actions/water-log/field.ts @@ -287,7 +287,7 @@ type FieldSession = | { ok: true; userId: string; brandId: string; role: "irrigator" | "water_admin" } | { ok: false; error: string }; -export async function requireFieldSession(): Promise { +async function requireFieldSession(): Promise { await getSession(); const cookieStore = await cookies(); const sessionId = cookieStore.get("wl_session")?.value; @@ -322,7 +322,7 @@ await getSession(); const cookieStore = await cookies(); }); } -export async function getFieldSessionUser(): Promise<{ +async function getFieldSessionUser(): Promise<{ userId: string; name: string; brandId: string; @@ -385,7 +385,7 @@ await getSession(); const cookieStore = await cookies(); cookieStore.delete("wl_admin_session"); } -export async function getWaterSession(): Promise { +async function getWaterSession(): Promise { await getSession(); const cookieStore = await cookies(); return cookieStore.get("wl_session")?.value ?? null; diff --git a/src/actions/wholesale-register.ts b/src/actions/wholesale-register.ts index cd5d21b..7e018c6 100644 --- a/src/actions/wholesale-register.ts +++ b/src/actions/wholesale-register.ts @@ -266,7 +266,7 @@ await getSession(); // Generate UUID at the start of checkout — before RPC ca }; } -export async function enqueueWholesaleWebhookForOrderCreated(orderId: string, brandId: string, customerId: string, invoiceNumber: string | null, subtotal: number) { +async function enqueueWholesaleWebhookForOrderCreated(orderId: string, brandId: string, customerId: string, invoiceNumber: string | null, subtotal: number) { await getSession(); try { await pool.query( diff --git a/src/auth.config.ts b/src/auth.config.ts index 00ddea1..966b460 100644 --- a/src/auth.config.ts +++ b/src/auth.config.ts @@ -63,7 +63,7 @@ export function getNeonAuthConfig(): NeonAuthBaseConfig { * Returns the Neon Auth base config, or null if not configured. * Use this for middleware that should gracefully degrade if auth is not set up. */ -export function getNeonAuthConfigOptional(): NeonAuthBaseConfig | null { +function getNeonAuthConfigOptional(): NeonAuthBaseConfig | null { const baseUrl = process.env.NEON_AUTH_BASE_URL || ""; const cookieSecret = process.env.NEON_AUTH_COOKIE_SECRET || ""; diff --git a/src/lib/ai-provider-models.ts b/src/lib/ai-provider-models.ts index bf8cfa7..efe55e8 100644 --- a/src/lib/ai-provider-models.ts +++ b/src/lib/ai-provider-models.ts @@ -2,7 +2,7 @@ export type AIProvider = "openai" | "anthropic" | "google" | "xai" | "minimax" | "custom"; -export const PROVIDER_MODELS: Record, string[]> = { +const PROVIDER_MODELS: Record, string[]> = { openai: ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-3.5-turbo"], // Note: claude-3-5-sonnet-* model IDs have been retired by Anthropic. // Current IDs are claude-sonnet-4-5, claude-sonnet-4-*, claude-opus-4-*, etc. @@ -37,6 +37,6 @@ export const DEFAULT_MODELS: Record, string> = { minimax: "MiniMax-M3", }; -export function getModelsForProvider(provider: Exclude): string[] { +function getModelsForProvider(provider: Exclude): string[] { return PROVIDER_MODELS[provider] ?? []; } \ No newline at end of file diff --git a/src/lib/analytics.ts b/src/lib/analytics.ts index 32f1484..e01f624 100644 --- a/src/lib/analytics.ts +++ b/src/lib/analytics.ts @@ -5,7 +5,7 @@ const posthogApiKey = process.env.NEXT_PUBLIC_POSTHOG_API_KEY; const posthogHost = process.env.NEXT_PUBLIC_POSTHOG_HOST || "https://app.posthog.com"; // Only enable in production or when API key is set -export const posthogEnabled = Boolean(posthogApiKey); +const posthogEnabled = Boolean(posthogApiKey); // Analytics helper functions export const analytics = { @@ -118,13 +118,13 @@ export const analytics = { }, }; -export function identifyUser(_userId: string, _properties?: Record) { +function identifyUser(_userId: string, _properties?: Record) { if (posthogEnabled && typeof window !== "undefined") { console.log("[Analytics] User identified"); } } -export function groupByBrand(_brandId: string, _properties?: Record) { +function groupByBrand(_brandId: string, _properties?: Record) { if (posthogEnabled && typeof window !== "undefined") { console.log("[Analytics] Grouped by brand"); } diff --git a/src/lib/brand-scope.ts b/src/lib/brand-scope.ts index e166504..2f47641 100644 --- a/src/lib/brand-scope.ts +++ b/src/lib/brand-scope.ts @@ -19,7 +19,7 @@ import "server-only"; import { cookies } from "next/headers"; import type { AdminUser } from "./admin-permissions-types"; -export const ACTIVE_BRAND_COOKIE = "active_brand_id"; +const ACTIVE_BRAND_COOKIE = "active_brand_id"; /** * Resolve the active brand id for the given admin user. diff --git a/src/lib/column-detector.ts b/src/lib/column-detector.ts index 6eb43de..e6d8b35 100644 --- a/src/lib/column-detector.ts +++ b/src/lib/column-detector.ts @@ -152,7 +152,7 @@ const FIELD_COLUMNS: Record> = { external_id: EXTERNAL_ID_COLUMNS, }; -export function detectColumnMappings( +function detectColumnMappings( headers: string[], rows: string[][] ): ColumnMapping[] { @@ -210,7 +210,7 @@ export function detectColumnMappings( return mappings; } -export function mapRowToContactEntry( +function mapRowToContactEntry( row: string[], mappings: ColumnMapping[] ): { diff --git a/src/lib/date-utils.ts b/src/lib/date-utils.ts index f3892bb..4ea6d68 100644 --- a/src/lib/date-utils.ts +++ b/src/lib/date-utils.ts @@ -14,7 +14,7 @@ export function formatDate(iso: string | null | undefined): string { /** * Format an ISO datetime string as "Jan 5, 2025 at 2:30 PM" */ -export function formatDateTime(iso: string | null | undefined): string { +function formatDateTime(iso: string | null | undefined): string { if (!iso) return "—"; const d = new Date(iso); if (isNaN(d.getTime())) return "—"; @@ -26,7 +26,7 @@ export function formatDateTime(iso: string | null | undefined): string { /** * Format an ISO date string as YYYY-MM-DD (for form inputs, export filenames) */ -export function formatDateISO(iso: string | null | undefined): string { +function formatDateISO(iso: string | null | undefined): string { if (!iso) return ""; const d = new Date(iso); if (isNaN(d.getTime())) return ""; @@ -36,7 +36,7 @@ export function formatDateISO(iso: string | null | undefined): string { /** * Format as YYYY-MM-DDTHH:MM for datetime-local input min values */ -export function formatDateTimeLocal(iso: string | null | undefined): string { +function formatDateTimeLocal(iso: string | null | undefined): string { if (!iso) return ""; const d = new Date(iso); if (isNaN(d.getTime())) return ""; @@ -46,7 +46,7 @@ export function formatDateTimeLocal(iso: string | null | undefined): string { /** * Relative time: "5m ago", "2h ago", "3d ago" */ -export function timeAgo(iso: string | null | undefined): string { +function timeAgo(iso: string | null | undefined): string { if (!iso) return "Never"; const diff = Date.now() - new Date(iso).getTime(); if (diff < 0) return "Just now"; @@ -66,14 +66,14 @@ export function timeAgo(iso: string | null | undefined): string { /** * Month name from 0-based index (for selector options) */ -export function getMonthName(monthIndex: number): string { +function getMonthName(monthIndex: number): string { return new Date(0, monthIndex).toLocaleString("en-US", { month: "long" }); } /** * Short month name: "Jan", "Feb" */ -export function getMonthNameShort(monthIndex: number): string { +function getMonthNameShort(monthIndex: number): string { return new Date(0, monthIndex).toLocaleString("en-US", { month: "short" }); } diff --git a/src/lib/feature-flags.ts b/src/lib/feature-flags.ts index da987b9..6854628 100644 --- a/src/lib/feature-flags.ts +++ b/src/lib/feature-flags.ts @@ -114,7 +114,7 @@ export const ADDON_CATALOG: Record = { }; // Core features that cannot be disabled -export const CORE_FEATURES: BrandFeatureKey[] = [ +const CORE_FEATURES: BrandFeatureKey[] = [ // Core platform features (always enabled) // These are not add-ons — the platform doesn't function without them ]; @@ -197,6 +197,6 @@ async function fetchBrandFeatures( /** * Invalidate cached features for a brand (call after feature toggle). */ -export function invalidateBrandFeatureCache(brandId: string): void { +function invalidateBrandFeatureCache(brandId: string): void { brandFeatureCache.delete(brandId); } \ No newline at end of file diff --git a/src/lib/format-date.ts b/src/lib/format-date.ts index 73a2de9..88f735a 100644 --- a/src/lib/format-date.ts +++ b/src/lib/format-date.ts @@ -28,7 +28,7 @@ export function formatDateTime(date: string | Date | null | undefined): string { }); } -export function formatTime(date: string | Date | null | undefined): string { +function formatTime(date: string | Date | null | undefined): string { if (!date) return "—"; const d = typeof date === "string" ? new Date(date) : date; if (isNaN(d.getTime())) return "—"; diff --git a/src/lib/pricing.ts b/src/lib/pricing.ts index de8c018..01a8c89 100644 --- a/src/lib/pricing.ts +++ b/src/lib/pricing.ts @@ -131,7 +131,7 @@ export type AddonKey = keyof typeof ADDONS; // ── Feature list per tier (for comparison table) ──────────────────────────── -export const TIER_FEATURE_MATRIX: Record = { +const TIER_FEATURE_MATRIX: Record = { starter: [ "Products catalog", "Stops management (10/mo)", @@ -168,35 +168,35 @@ export const TIER_FEATURE_MATRIX: Record = { // ── Utility functions ──────────────────────────────────────────────────────── -export function formatPrice(amount: number | null): string { +function formatPrice(amount: number | null): string { if (amount === null) return "Custom"; return `$${amount.toFixed(0)}`; } -export function formatPricePerMonth(amount: number | null): string { +function formatPricePerMonth(amount: number | null): string { if (amount === null) return "Custom"; return `$${amount}/mo`; } -export function getPlanMonthlyPrice(tier: PlanTierKey): number | null { +function getPlanMonthlyPrice(tier: PlanTierKey): number | null { return PLAN_TIERS[tier]?.monthlyPrice ?? null; } -export function getPlanAnnualPrice(tier: PlanTierKey): number | null { +function getPlanAnnualPrice(tier: PlanTierKey): number | null { return PLAN_TIERS[tier]?.annualPrice ?? null; } -export function getAddonMonthlyPrice(addon: AddonKey): number { +function getAddonMonthlyPrice(addon: AddonKey): number { return ADDONS[addon]?.monthlyPrice ?? 0; } -export function getAddonAnnualPrice(addon: AddonKey): number { +function getAddonAnnualPrice(addon: AddonKey): number { return ADDONS[addon]?.annualPrice ?? 0; } -export function calculateAnnualSavings(monthlyTotal: number): number { +function calculateAnnualSavings(monthlyTotal: number): number { return Math.round(monthlyTotal * 0.25); // 25% annual discount } -export const BILLING_COMPANY_NAME = "Cielo Hermosa, LLC"; -export const BILLING_EMAIL = "billing@cielohermosa.com"; \ No newline at end of file +const BILLING_COMPANY_NAME = "Cielo Hermosa, LLC"; +const BILLING_EMAIL = "billing@cielohermosa.com"; \ No newline at end of file diff --git a/src/lib/pwa.ts b/src/lib/pwa.ts index 2e88a7a..d1efbc9 100644 --- a/src/lib/pwa.ts +++ b/src/lib/pwa.ts @@ -31,13 +31,13 @@ export function registerServiceWorker() { }); } -export async function subscribeToPush(_registration: ServiceWorkerRegistration) { +async function subscribeToPush(_registration: ServiceWorkerRegistration) { // Push subscription disabled for now - requires VAPID key setup return null; } // Check if app is installed (PWA) -export function isPWAInstalled(): boolean { +function isPWAInstalled(): boolean { if (typeof window === 'undefined') return false; return ( window.matchMedia('(display-mode: standalone)').matches || @@ -46,7 +46,7 @@ export function isPWAInstalled(): boolean { } // Share API for native sharing -export async function shareContent(content: { +async function shareContent(content: { title: string; text: string; url?: string; diff --git a/src/lib/rate-limit.ts b/src/lib/rate-limit.ts index 9b1077a..0f6a4dd 100644 --- a/src/lib/rate-limit.ts +++ b/src/lib/rate-limit.ts @@ -76,7 +76,7 @@ export function securityHeaders() { } as const; } -export function rateLimitHeaders(rateCheck: { remaining: number; reset: number }) { +function rateLimitHeaders(rateCheck: { remaining: number; reset: number }) { return { "X-RateLimit-Limit": String(100), "X-RateLimit-Remaining": String(rateCheck.remaining), @@ -86,6 +86,6 @@ export function rateLimitHeaders(rateCheck: { remaining: number; reset: number } // Pre-configured limiters export const apiLimiter = RATE_LIMITS.api; -export const checkoutLimiter = RATE_LIMITS.checkout; +const checkoutLimiter = RATE_LIMITS.checkout; export const emailLimiter = RATE_LIMITS.email; -export const authLimiter = RATE_LIMITS.auth; \ No newline at end of file +const authLimiter = RATE_LIMITS.auth; \ No newline at end of file diff --git a/src/lib/sentry.ts b/src/lib/sentry.ts index 8a24046..901ec13 100644 --- a/src/lib/sentry.ts +++ b/src/lib/sentry.ts @@ -53,7 +53,7 @@ export const captureError = (error: Error, context?: Record) => }; // Export for manual breadcrumb logging -export const addBreadcrumb = (message: string, data?: Record) => { +const addBreadcrumb = (message: string, data?: Record) => { Sentry.addBreadcrumb({ message, data, @@ -62,7 +62,7 @@ export const addBreadcrumb = (message: string, data?: Record) = }; // Export for user tracking during errors -export const setUserContext = (userId: string, brandId?: string) => { +const setUserContext = (userId: string, brandId?: string) => { Sentry.setUser({ id: userId, tags: { brand_id: brandId || "unknown" }, @@ -70,7 +70,7 @@ export const setUserContext = (userId: string, brandId?: string) => { }; // Export for transaction tracing - simplified wrapper -export async function withTransaction( +async function withTransaction( name: string, op: string, fn: () => Promise diff --git a/src/lib/server-log.ts b/src/lib/server-log.ts index 6e73e9d..d5cba54 100644 --- a/src/lib/server-log.ts +++ b/src/lib/server-log.ts @@ -17,7 +17,7 @@ export function serverLog(...args: unknown[]): void { console.log(...args); } -export function serverInfo(...args: unknown[]): void { +function serverInfo(...args: unknown[]): void { console.info(...args); } diff --git a/src/lib/storage.ts b/src/lib/storage.ts index 4491e93..e8a5279 100644 --- a/src/lib/storage.ts +++ b/src/lib/storage.ts @@ -22,7 +22,7 @@ import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; let _client: S3Client | null = null; -export function getStorageClient(): S3Client { +function getStorageClient(): S3Client { if (_client) return _client; const endpoint = process.env.MINIO_ENDPOINT ?? "s3.crispygoat.com"; @@ -48,7 +48,7 @@ export function getStorageClient(): S3Client { * Returns the public base URL for the given bucket. * Override with MINIO_PUBLIC_URL if your public endpoint differs from the endpoint. */ -export function getPublicUrl(bucket: string, key: string): string { +function getPublicUrl(bucket: string, key: string): string { const base = process.env.MINIO_PUBLIC_URL ?? `https://${process.env.MINIO_ENDPOINT ?? "s3.crispygoat.com"}`; return `${base}/${bucket}/${key}`; } @@ -81,7 +81,7 @@ export async function uploadObject(opts: UploadOptions): Promise { return getPublicUrl(opts.bucket, opts.key); } -export async function deleteObject(bucket: string, key: string): Promise { +async function deleteObject(bucket: string, key: string): Promise { const client = getStorageClient(); await client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key })); } @@ -98,7 +98,7 @@ export async function deleteObject(bucket: string, key: string): Promise { * @param contentType Expected MIME type * @param expiresSeconds URL expiry (default 5 minutes) */ -export async function getPresignedPutUrl( +async function getPresignedPutUrl( bucket: string, key: string, contentType: string, @@ -118,7 +118,7 @@ export async function getPresignedPutUrl( * @param key Object key * @param expiresSeconds URL expiry (default 1 hour) */ -export async function getPresignedGetUrl( +async function getPresignedGetUrl( bucket: string, key: string, expiresSeconds = 3600 diff --git a/src/lib/stripe-billing.ts b/src/lib/stripe-billing.ts index 4dd1963..e102889 100644 --- a/src/lib/stripe-billing.ts +++ b/src/lib/stripe-billing.ts @@ -33,7 +33,7 @@ export const stripe = { }; // Plan tier configurations -export const PLANS = { +const PLANS = { starter: { id: "starter", name: "Starter", @@ -99,7 +99,7 @@ export const PLANS = { } as const; // Add-ons configuration -export const ADDONS = { +const ADDONS = { wholesale_portal: { id: "wholesale_portal", name: "Wholesale Portal", @@ -168,7 +168,7 @@ export interface CreateSubscriptionOptions { metadata?: Record; } -export async function createSubscription(options: CreateSubscriptionOptions) { +async function createSubscription(options: CreateSubscriptionOptions) { const { brandId, brandName, email, plan, interval, successUrl, cancelUrl, metadata } = options; // Get or create Stripe customer @@ -212,7 +212,7 @@ export async function createSubscription(options: CreateSubscriptionOptions) { return session; } -export async function createAddonSubscription(options: { +async function createAddonSubscription(options: { customerId: string; addon: keyof typeof ADDONS; brandId: string; @@ -256,7 +256,7 @@ export async function createAddonSubscription(options: { // CUSTOMER PORTAL // ============================================ -export async function createCustomerPortalSession(options: { +async function createCustomerPortalSession(options: { customerId: string; returnUrl: string; }) { @@ -274,7 +274,7 @@ export async function createCustomerPortalSession(options: { // SUBSCRIPTION UPDATES // ============================================ -export async function cancelSubscription(subscriptionId: string, immediate: boolean = false) { +async function cancelSubscription(subscriptionId: string, immediate: boolean = false) { if (immediate) { return await stripe.subscriptions.cancel(subscriptionId); } @@ -284,13 +284,13 @@ export async function cancelSubscription(subscriptionId: string, immediate: bool }); } -export async function reactivateSubscription(subscriptionId: string) { +async function reactivateSubscription(subscriptionId: string) { return await stripe.subscriptions.update(subscriptionId, { cancel_at_period_end: false, }); } -export async function changePlan(subscriptionId: string, newPlan: keyof typeof PLANS, interval: "monthly" | "annual") { +async function changePlan(subscriptionId: string, newPlan: keyof typeof PLANS, interval: "monthly" | "annual") { const subscription = await stripe.subscriptions.retrieve(subscriptionId); // Get new price ID @@ -314,7 +314,7 @@ export async function changePlan(subscriptionId: string, newPlan: keyof typeof P // CUSTOMER MANAGEMENT // ============================================ -export async function getOrCreateCustomer(brandId: string, name: string, email: string) { +async function getOrCreateCustomer(brandId: string, name: string, email: string) { // Check if customer exists for this brand const existingCustomers = await stripe.customers.list({ email, @@ -337,11 +337,11 @@ export async function getOrCreateCustomer(brandId: string, name: string, email: return customer.id; } -export async function getCustomer(customerId: string) { +async function getCustomer(customerId: string) { return await stripe.customers.retrieve(customerId); } -export async function updateCustomer(customerId: string, data: Partial) { +async function updateCustomer(customerId: string, data: Partial) { return await stripe.customers.update(customerId, data); } @@ -349,20 +349,20 @@ export async function updateCustomer(customerId: string, data: Partial