@promakeai/cli 0.3.2 → 0.3.3
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/dist/index.js +415 -209
- package/dist/registry/about-page.json +3 -3
- package/dist/registry/about-section.json +4 -4
- package/dist/registry/animations.json +2 -2
- package/dist/registry/announcement-bar.json +4 -4
- package/dist/registry/api.json +1 -1
- package/dist/registry/auth-core.json +3 -3
- package/dist/registry/auth.json +70 -0
- package/dist/registry/bento-grid-section.json +4 -4
- package/dist/registry/blog-core.json +5 -5
- package/dist/registry/blog-list-page.json +3 -3
- package/dist/registry/blog-section.json +4 -4
- package/dist/registry/cards-carousel-section.json +4 -4
- package/dist/registry/cart-drawer.json +3 -3
- package/dist/registry/cart-page.json +3 -3
- package/dist/registry/case-study-page.json +3 -3
- package/dist/registry/category-section.json +3 -3
- package/dist/registry/checkout-page.json +3 -3
- package/dist/registry/coming-soon-page-minimal.json +4 -4
- package/dist/registry/coming-soon-page.json +4 -4
- package/dist/registry/contact-info-grid.json +4 -4
- package/dist/registry/contact-page-centered.json +4 -4
- package/dist/registry/contact-page-split.json +4 -4
- package/dist/registry/contact-page.json +3 -3
- package/dist/registry/content-section.json +4 -4
- package/dist/registry/cookie-consent.json +4 -4
- package/dist/registry/cookies-page.json +3 -3
- package/dist/registry/cta-section.json +3 -3
- package/dist/registry/docs/reset-password-page.md +36 -0
- package/dist/registry/ecommerce-core.json +10 -10
- package/dist/registry/empty-page.json +3 -3
- package/dist/registry/faq-categorized.json +4 -4
- package/dist/registry/faq-simple.json +4 -4
- package/dist/registry/favorites-blog-block.json +1 -1
- package/dist/registry/favorites-blog-page.json +4 -4
- package/dist/registry/favorites-ecommerce-block.json +1 -1
- package/dist/registry/favorites-ecommerce-page.json +4 -4
- package/dist/registry/feature-section.json +3 -3
- package/dist/registry/featured-products.json +3 -3
- package/dist/registry/footer-detailed.json +4 -4
- package/dist/registry/footer-minimal.json +3 -3
- package/dist/registry/footer.json +3 -3
- package/dist/registry/forgot-password-page-split.json +4 -4
- package/dist/registry/forgot-password-page.json +4 -4
- package/dist/registry/google-adsense.json +4 -4
- package/dist/registry/google-map.json +2 -2
- package/dist/registry/header-centered-pill.json +4 -4
- package/dist/registry/header-ecommerce.json +3 -3
- package/dist/registry/header-mega.json +4 -4
- package/dist/registry/header-minimal.json +4 -4
- package/dist/registry/header-simple.json +3 -3
- package/dist/registry/hero-carousel.json +3 -3
- package/dist/registry/hero-cta.json +4 -4
- package/dist/registry/hero-gradient.json +4 -4
- package/dist/registry/hero-grid.json +4 -4
- package/dist/registry/hero-profile.json +3 -3
- package/dist/registry/hero.json +3 -3
- package/dist/registry/landing-page-app.json +3 -3
- package/dist/registry/landing-page-saas.json +3 -3
- package/dist/registry/login-page-split.json +4 -4
- package/dist/registry/login-page.json +4 -4
- package/dist/registry/logo-cloud.json +4 -4
- package/dist/registry/masonry-grid.json +3 -3
- package/dist/registry/my-orders-page.json +4 -4
- package/dist/registry/newsletter-section.json +4 -4
- package/dist/registry/order-card-compact.json +1 -1
- package/dist/registry/order-confirmation-page.json +4 -4
- package/dist/registry/order-detail-block.json +1 -1
- package/dist/registry/orders-list-block.json +1 -1
- package/dist/registry/payment-success-block.json +1 -1
- package/dist/registry/portfolio-page.json +4 -4
- package/dist/registry/post-card.json +3 -3
- package/dist/registry/post-detail-block.json +1 -1
- package/dist/registry/post-detail-page.json +4 -4
- package/dist/registry/pricing-card.json +3 -3
- package/dist/registry/pricing-page.json +4 -4
- package/dist/registry/pricing-section.json +4 -4
- package/dist/registry/privacy-page.json +3 -3
- package/dist/registry/product-card-detailed.json +4 -4
- package/dist/registry/product-card-hover.json +4 -4
- package/dist/registry/product-card.json +3 -3
- package/dist/registry/product-detail-block.json +1 -1
- package/dist/registry/product-detail-page.json +4 -4
- package/dist/registry/product-detail-section.json +4 -4
- package/dist/registry/product-quick-view.json +4 -4
- package/dist/registry/products-page.json +3 -3
- package/dist/registry/reading-progress.json +4 -4
- package/dist/registry/register-page-split.json +4 -4
- package/dist/registry/register-page.json +4 -4
- package/dist/registry/related-posts-block.json +1 -1
- package/dist/registry/related-products-block.json +1 -1
- package/dist/registry/reset-password-page-split.json +4 -4
- package/dist/registry/reset-password-page.json +39 -0
- package/dist/registry/service-card.json +1 -1
- package/dist/registry/share-buttons.json +4 -4
- package/dist/registry/skill-card.json +1 -1
- package/dist/registry/team-page.json +4 -4
- package/dist/registry/terms-page.json +3 -3
- package/dist/registry/testimonials-carousel.json +4 -4
- package/dist/registry/testimonials-grid.json +4 -4
- package/dist/registry/timeline-section.json +4 -4
- package/dist/registry/video-hero.json +4 -4
- package/dist/registry/youtube-embed.json +4 -4
- package/package.json +2 -2
- package/template/.env +6 -6
- package/template/public/_redirects +1 -1
- package/template/public/robots.txt +14 -14
- package/template/src/components/GoogleAnalytics.tsx +34 -34
- package/template/src/components/LanguageSwitcher.tsx +53 -53
- package/template/src/components/ScriptInjector.tsx +62 -62
- package/template/src/lib/env.ts +19 -19
- package/template/src/router.tsx +14 -14
- package/template/src/vite-env.d.ts +1 -1
|
@@ -13,61 +13,61 @@
|
|
|
13
13
|
"path": "ecommerce-core/index.ts",
|
|
14
14
|
"type": "registry:index",
|
|
15
15
|
"target": "$modules$/ecommerce-core/index.ts",
|
|
16
|
-
"content": "// Types\nexport * from './types';\n\n// Stores (Zustand)\nexport { useCartStore, useCart } from './stores/cart-store';\nexport { useFavoritesStore, useFavorites } from './stores/favorites-store';\n\n// Hooks\nexport { useProducts, useProductBySlug, useFeaturedProducts, useCategories } from './useProducts';\nexport { useSearch } from './useSearch';\n\n// Utilities\nexport { formatPrice } from './format-price';\n\n// Payment Config\nexport {\n type PaymentMethod,\n type OnlinePaymentProvider,\n type PaymentMethodConfig,\n PAYMENT_METHOD_CONFIGS,\n ONLINE_PROVIDER_CONFIGS,\n getAvailablePaymentMethods,\n getOnlinePaymentProviders,\n getFilteredPaymentMethodConfigs,\n isPaymentMethodAvailable,\n isOnlineProviderAvailable,\n} from './payment-config';\n"
|
|
16
|
+
"content": "// Types\r\nexport * from './types';\r\n\r\n// Stores (Zustand)\r\nexport { useCartStore, useCart } from './stores/cart-store';\r\nexport { useFavoritesStore, useFavorites } from './stores/favorites-store';\r\n\r\n// Hooks\r\nexport { useProducts, useProductBySlug, useFeaturedProducts, useCategories } from './useProducts';\r\nexport { useSearch } from './useSearch';\r\n\r\n// Utilities\r\nexport { formatPrice } from './format-price';\r\n\r\n// Payment Config\r\nexport {\r\n type PaymentMethod,\r\n type OnlinePaymentProvider,\r\n type PaymentMethodConfig,\r\n PAYMENT_METHOD_CONFIGS,\r\n ONLINE_PROVIDER_CONFIGS,\r\n getAvailablePaymentMethods,\r\n getOnlinePaymentProviders,\r\n getFilteredPaymentMethodConfigs,\r\n isPaymentMethodAvailable,\r\n isOnlineProviderAvailable,\r\n} from './payment-config';\r\n"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "ecommerce-core/types.ts",
|
|
20
20
|
"type": "registry:type",
|
|
21
21
|
"target": "$modules$/ecommerce-core/types.ts",
|
|
22
|
-
"content": "export interface ProductCategory {\n id: number;\n name: string;\n slug: string;\n is_primary: boolean;\n}\n\nexport interface Product {\n id: number;\n name: string;\n slug: string;\n description: string;\n price: number;\n sale_price?: number;\n on_sale: boolean;\n images: string[];\n category: string; // Primary category slug (backward compatibility)\n category_name?: string; // Primary category name (backward compatibility)\n categories: ProductCategory[]; // NEW: Multi-category support\n brand?: string;\n sku?: string;\n stock: number;\n tags: string[];\n rating: number;\n review_count: number;\n featured: boolean;\n is_new: boolean;\n published: boolean;\n specifications?: Record<string, any>;\n variants?: any;\n created_at?: string;\n updated_at?: string;\n meta_description?: string;\n meta_keywords?: string;\n}\n\nexport interface ProductVariant {\n id: string;\n name: string;\n value: string;\n price?: number;\n image?: string;\n stockQuantity: number;\n}\n\nexport interface CartItem {\n id: string | number;\n product: Product;\n quantity: number;\n}\n\nexport interface CartState {\n items: CartItem[];\n total: number;\n}\n\nexport interface CartContextType {\n state: CartState;\n addItem: (product: Product) => void;\n removeItem: (id: string | number) => void;\n updateQuantity: (id: string | number, quantity: number) => void;\n clearCart: () => void;\n itemCount: number;\n}\n\nexport interface FavoritesContextType {\n favorites: Product[];\n addToFavorites: (product: Product) => void;\n removeFromFavorites: (productId: string | number) => void;\n isFavorite: (productId: string | number) => boolean;\n favoriteCount: number;\n clearFavorites: () => void;\n}\n\nexport interface Category {\n id: number;\n name: string;\n slug: string;\n description?: string;\n image?: string;\n}\n\nexport interface User {\n id: string;\n email: string;\n name: string;\n avatar?: string;\n addresses?: Address[];\n orders?: Order[];\n}\n\nexport interface Address {\n name: string;\n line1: string;\n line2?: string;\n city: string;\n state: string;\n postalCode: string;\n country: string;\n}\n\nexport interface Order {\n id: number;\n user_id: string;\n total_price: number;\n status: 'pending' | 'confirmed' | 'shipped' | 'delivered' | 'cancelled';\n payment_method: string;\n shipping_address: Address;\n notes?: string;\n created_at?: string;\n}\n\nexport interface OrderItem {\n id: number;\n order_id: number;\n product_id: number;\n quantity: number;\n price: number;\n product?: {\n name: string;\n slug: string;\n images: string[];\n price: number;\n };\n}\n"
|
|
22
|
+
"content": "export interface ProductCategory {\r\n id: number;\r\n name: string;\r\n slug: string;\r\n is_primary: boolean;\r\n}\r\n\r\nexport interface Product {\r\n id: number;\r\n name: string;\r\n slug: string;\r\n description: string;\r\n price: number;\r\n sale_price?: number;\r\n on_sale: boolean;\r\n images: string[];\r\n category: string; // Primary category slug (backward compatibility)\r\n category_name?: string; // Primary category name (backward compatibility)\r\n categories: ProductCategory[]; // NEW: Multi-category support\r\n brand?: string;\r\n sku?: string;\r\n stock: number;\r\n tags: string[];\r\n rating: number;\r\n review_count: number;\r\n featured: boolean;\r\n is_new: boolean;\r\n published: boolean;\r\n specifications?: Record<string, any>;\r\n variants?: any;\r\n created_at?: string;\r\n updated_at?: string;\r\n meta_description?: string;\r\n meta_keywords?: string;\r\n}\r\n\r\nexport interface ProductVariant {\r\n id: string;\r\n name: string;\r\n value: string;\r\n price?: number;\r\n image?: string;\r\n stockQuantity: number;\r\n}\r\n\r\nexport interface CartItem {\r\n id: string | number;\r\n product: Product;\r\n quantity: number;\r\n}\r\n\r\nexport interface CartState {\r\n items: CartItem[];\r\n total: number;\r\n}\r\n\r\nexport interface CartContextType {\r\n state: CartState;\r\n addItem: (product: Product) => void;\r\n removeItem: (id: string | number) => void;\r\n updateQuantity: (id: string | number, quantity: number) => void;\r\n clearCart: () => void;\r\n itemCount: number;\r\n}\r\n\r\nexport interface FavoritesContextType {\r\n favorites: Product[];\r\n addToFavorites: (product: Product) => void;\r\n removeFromFavorites: (productId: string | number) => void;\r\n isFavorite: (productId: string | number) => boolean;\r\n favoriteCount: number;\r\n clearFavorites: () => void;\r\n}\r\n\r\nexport interface Category {\r\n id: number;\r\n name: string;\r\n slug: string;\r\n description?: string;\r\n image?: string;\r\n}\r\n\r\nexport interface User {\r\n id: string;\r\n email: string;\r\n name: string;\r\n avatar?: string;\r\n addresses?: Address[];\r\n orders?: Order[];\r\n}\r\n\r\nexport interface Address {\r\n name: string;\r\n line1: string;\r\n line2?: string;\r\n city: string;\r\n state: string;\r\n postalCode: string;\r\n country: string;\r\n}\r\n\r\nexport interface Order {\r\n id: number;\r\n user_id: string;\r\n total_price: number;\r\n status: 'pending' | 'confirmed' | 'shipped' | 'delivered' | 'cancelled';\r\n payment_method: string;\r\n shipping_address: Address;\r\n notes?: string;\r\n created_at?: string;\r\n}\r\n\r\nexport interface OrderItem {\r\n id: number;\r\n order_id: number;\r\n product_id: number;\r\n quantity: number;\r\n price: number;\r\n product?: {\r\n name: string;\r\n slug: string;\r\n images: string[];\r\n price: number;\r\n };\r\n}\r\n"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"path": "ecommerce-core/stores/cart-store.ts",
|
|
26
26
|
"type": "registry:store",
|
|
27
27
|
"target": "$modules$/ecommerce-core/stores/cart-store.ts",
|
|
28
|
-
"content": "import { create } from \"zustand\";\nimport { persist } from \"zustand/middleware\";\nimport type { Product, CartItem, CartState, CartContextType } from \"../types\";\n\nconst getProductPrice = (product: Product): number => {\n return product.on_sale && product.sale_price ? product.sale_price : product.price;\n};\n\nconst calculateTotal = (items: CartItem[]): number => {\n return items.reduce((total, item) => {\n const price = getProductPrice(item.product);\n return total + price * item.quantity;\n }, 0);\n};\n\ninterface CartStore extends CartState {\n addItem: (product: Product) => void;\n removeItem: (id: string | number) => void;\n updateQuantity: (id: string | number, quantity: number) => void;\n clearCart: () => void;\n itemCount: number;\n isDrawerOpen: boolean;\n setDrawerOpen: (open: boolean) => void;\n}\n\nexport const useCartStore = create<CartStore>()(\n persist(\n (set, _get) => ({\n items: [],\n total: 0,\n itemCount: 0,\n isDrawerOpen: false,\n setDrawerOpen: (open: boolean) => set({ isDrawerOpen: open }),\n\n addItem: (product) =>\n set((state) => {\n const existingItem = state.items.find(\n (item) => item.product.id === product.id\n );\n\n if (existingItem) {\n const items = state.items.map((item) =>\n item.product.id === product.id\n ? { ...item, quantity: item.quantity + 1 }\n : item\n );\n return {\n items,\n total: calculateTotal(items),\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\n isDrawerOpen: true,\n };\n }\n\n const items = [\n ...state.items,\n { id: product.id, product, quantity: 1 },\n ];\n return {\n items,\n total: calculateTotal(items),\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\n isDrawerOpen: true,\n };\n }),\n\n removeItem: (id) =>\n set((state) => {\n const items = state.items.filter((item) => item.id !== id);\n return {\n items,\n total: calculateTotal(items),\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\n };\n }),\n\n updateQuantity: (id, quantity) =>\n set((state) => {\n if (quantity <= 0) {\n const items = state.items.filter((item) => item.id !== id);\n return {\n items,\n total: calculateTotal(items),\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\n };\n }\n\n const items = state.items.map((item) =>\n item.id === id ? { ...item, quantity } : item\n );\n return {\n items,\n total: calculateTotal(items),\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\n };\n }),\n\n clearCart: () => set({ items: [], total: 0, itemCount: 0 }),\n }),\n { name: \"ecommerce_cart\" }\n )\n);\n\n// Backward compatible hook - matches CartContextType with drawer state\nexport const useCart = (): CartContextType & { isDrawerOpen: boolean; setDrawerOpen: (open: boolean) => void } => {\n const store = useCartStore();\n return {\n state: { items: store.items, total: store.total },\n addItem: store.addItem,\n removeItem: store.removeItem,\n updateQuantity: store.updateQuantity,\n clearCart: store.clearCart,\n itemCount: store.itemCount,\n isDrawerOpen: store.isDrawerOpen,\n setDrawerOpen: store.setDrawerOpen,\n };\n};\n"
|
|
28
|
+
"content": "import { create } from \"zustand\";\r\nimport { persist } from \"zustand/middleware\";\r\nimport type { Product, CartItem, CartState, CartContextType } from \"../types\";\r\n\r\nconst getProductPrice = (product: Product): number => {\r\n return product.on_sale && product.sale_price ? product.sale_price : product.price;\r\n};\r\n\r\nconst calculateTotal = (items: CartItem[]): number => {\r\n return items.reduce((total, item) => {\r\n const price = getProductPrice(item.product);\r\n return total + price * item.quantity;\r\n }, 0);\r\n};\r\n\r\ninterface CartStore extends CartState {\r\n addItem: (product: Product) => void;\r\n removeItem: (id: string | number) => void;\r\n updateQuantity: (id: string | number, quantity: number) => void;\r\n clearCart: () => void;\r\n itemCount: number;\r\n isDrawerOpen: boolean;\r\n setDrawerOpen: (open: boolean) => void;\r\n}\r\n\r\nexport const useCartStore = create<CartStore>()(\r\n persist(\r\n (set, _get) => ({\r\n items: [],\r\n total: 0,\r\n itemCount: 0,\r\n isDrawerOpen: false,\r\n setDrawerOpen: (open: boolean) => set({ isDrawerOpen: open }),\r\n\r\n addItem: (product) =>\r\n set((state) => {\r\n const existingItem = state.items.find(\r\n (item) => item.product.id === product.id\r\n );\r\n\r\n if (existingItem) {\r\n const items = state.items.map((item) =>\r\n item.product.id === product.id\r\n ? { ...item, quantity: item.quantity + 1 }\r\n : item\r\n );\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n isDrawerOpen: true,\r\n };\r\n }\r\n\r\n const items = [\r\n ...state.items,\r\n { id: product.id, product, quantity: 1 },\r\n ];\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n isDrawerOpen: true,\r\n };\r\n }),\r\n\r\n removeItem: (id) =>\r\n set((state) => {\r\n const items = state.items.filter((item) => item.id !== id);\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n };\r\n }),\r\n\r\n updateQuantity: (id, quantity) =>\r\n set((state) => {\r\n if (quantity <= 0) {\r\n const items = state.items.filter((item) => item.id !== id);\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n };\r\n }\r\n\r\n const items = state.items.map((item) =>\r\n item.id === id ? { ...item, quantity } : item\r\n );\r\n return {\r\n items,\r\n total: calculateTotal(items),\r\n itemCount: items.reduce((sum, i) => sum + i.quantity, 0),\r\n };\r\n }),\r\n\r\n clearCart: () => set({ items: [], total: 0, itemCount: 0 }),\r\n }),\r\n { name: \"ecommerce_cart\" }\r\n )\r\n);\r\n\r\n// Backward compatible hook - matches CartContextType with drawer state\r\nexport const useCart = (): CartContextType & { isDrawerOpen: boolean; setDrawerOpen: (open: boolean) => void } => {\r\n const store = useCartStore();\r\n return {\r\n state: { items: store.items, total: store.total },\r\n addItem: store.addItem,\r\n removeItem: store.removeItem,\r\n updateQuantity: store.updateQuantity,\r\n clearCart: store.clearCart,\r\n itemCount: store.itemCount,\r\n isDrawerOpen: store.isDrawerOpen,\r\n setDrawerOpen: store.setDrawerOpen,\r\n };\r\n};\r\n"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"path": "ecommerce-core/stores/favorites-store.ts",
|
|
32
32
|
"type": "registry:store",
|
|
33
33
|
"target": "$modules$/ecommerce-core/stores/favorites-store.ts",
|
|
34
|
-
"content": "import { create } from \"zustand\";\nimport { persist } from \"zustand/middleware\";\nimport type { Product, FavoritesContextType } from \"../types\";\n\ninterface FavoritesStore {\n favorites: Product[];\n favoriteCount: number;\n addToFavorites: (product: Product) => void;\n removeFromFavorites: (productId: string | number) => void;\n isFavorite: (productId: string | number) => boolean;\n clearFavorites: () => void;\n}\n\nexport const useFavoritesStore = create<FavoritesStore>()(\n persist(\n (set, get) => ({\n favorites: [],\n favoriteCount: 0,\n\n addToFavorites: (product) =>\n set((state) => {\n if (state.favorites.some((fav) => fav.id === product.id)) {\n return state;\n }\n const favorites = [...state.favorites, product];\n return { favorites, favoriteCount: favorites.length };\n }),\n\n removeFromFavorites: (productId) =>\n set((state) => {\n const favorites = state.favorites.filter((fav) => fav.id !== productId);\n return { favorites, favoriteCount: favorites.length };\n }),\n\n isFavorite: (productId) => {\n return get().favorites.some((fav) => fav.id === productId);\n },\n\n clearFavorites: () => set({ favorites: [], favoriteCount: 0 }),\n }),\n { name: \"ecommerce_favorites\" }\n )\n);\n\n// Backward compatible hook - matches FavoritesContextType\nexport const useFavorites = (): FavoritesContextType => {\n const store = useFavoritesStore();\n return {\n favorites: store.favorites,\n addToFavorites: store.addToFavorites,\n removeFromFavorites: store.removeFromFavorites,\n isFavorite: store.isFavorite,\n favoriteCount: store.favoriteCount,\n clearFavorites: store.clearFavorites,\n };\n};\n"
|
|
34
|
+
"content": "import { create } from \"zustand\";\r\nimport { persist } from \"zustand/middleware\";\r\nimport type { Product, FavoritesContextType } from \"../types\";\r\n\r\ninterface FavoritesStore {\r\n favorites: Product[];\r\n favoriteCount: number;\r\n addToFavorites: (product: Product) => void;\r\n removeFromFavorites: (productId: string | number) => void;\r\n isFavorite: (productId: string | number) => boolean;\r\n clearFavorites: () => void;\r\n}\r\n\r\nexport const useFavoritesStore = create<FavoritesStore>()(\r\n persist(\r\n (set, get) => ({\r\n favorites: [],\r\n favoriteCount: 0,\r\n\r\n addToFavorites: (product) =>\r\n set((state) => {\r\n if (state.favorites.some((fav) => fav.id === product.id)) {\r\n return state;\r\n }\r\n const favorites = [...state.favorites, product];\r\n return { favorites, favoriteCount: favorites.length };\r\n }),\r\n\r\n removeFromFavorites: (productId) =>\r\n set((state) => {\r\n const favorites = state.favorites.filter((fav) => fav.id !== productId);\r\n return { favorites, favoriteCount: favorites.length };\r\n }),\r\n\r\n isFavorite: (productId) => {\r\n return get().favorites.some((fav) => fav.id === productId);\r\n },\r\n\r\n clearFavorites: () => set({ favorites: [], favoriteCount: 0 }),\r\n }),\r\n { name: \"ecommerce_favorites\" }\r\n )\r\n);\r\n\r\n// Backward compatible hook - matches FavoritesContextType\r\nexport const useFavorites = (): FavoritesContextType => {\r\n const store = useFavoritesStore();\r\n return {\r\n favorites: store.favorites,\r\n addToFavorites: store.addToFavorites,\r\n removeFromFavorites: store.removeFromFavorites,\r\n isFavorite: store.isFavorite,\r\n favoriteCount: store.favoriteCount,\r\n clearFavorites: store.clearFavorites,\r\n };\r\n};\r\n"
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
37
|
"path": "ecommerce-core/useProducts.ts",
|
|
38
38
|
"type": "registry:hook",
|
|
39
39
|
"target": "$modules$/ecommerce-core/useProducts.ts",
|
|
40
|
-
"content": "import { useMemo } from 'react';\nimport type { Product, Category, ProductCategory } from './types';\nimport {\n useRepositoryQuery,\n useRawQuery,\n useRawQueryOne,\n parseStringToArray,\n parseJSONString,\n parseSQLiteBoolean,\n parseNumberSafe\n} from '@/modules/db';\n\nconst transformProduct = (row: any): Product => {\n const categoryNames = row.category_names ? row.category_names.split(',') : [];\n const categorySlugs = row.category_slugs ? row.category_slugs.split(',') : [];\n const categoryIds = row.category_ids ? row.category_ids.split(',').map(Number) : [];\n\n const categories: ProductCategory[] = categoryIds.map((id: number, index: number) => ({\n id,\n name: categoryNames[index] || '',\n slug: categorySlugs[index] || '',\n is_primary: index === 0\n }));\n\n const primaryCategory = categories.length > 0 ? categories[0] : null;\n\n return {\n id: parseNumberSafe(row.id),\n name: String(row.name || ''),\n slug: String(row.slug || ''),\n description: row.description || '',\n price: parseNumberSafe(row.price),\n sale_price: row.sale_price ? parseNumberSafe(row.sale_price) : undefined,\n on_sale: parseSQLiteBoolean(row.on_sale),\n images: parseStringToArray(row.images),\n brand: row.brand || '',\n sku: row.sku || '',\n stock: parseNumberSafe(row.stock),\n tags: parseJSONString(row.tags, []) || [],\n rating: parseNumberSafe(row.rating),\n review_count: parseNumberSafe(row.review_count),\n featured: parseSQLiteBoolean(row.featured),\n is_new: parseSQLiteBoolean(row.is_new),\n published: parseSQLiteBoolean(row.published),\n specifications: parseJSONString(row.specifications, {}) || {},\n variants: parseJSONString(row.variants, []) || [],\n created_at: row.created_at || new Date().toISOString(),\n updated_at: row.updated_at || new Date().toISOString(),\n meta_description: row.meta_description || '',\n meta_keywords: row.meta_keywords || '',\n category: primaryCategory?.slug || '',\n category_name: primaryCategory?.name || '',\n categories\n };\n};\n\nconst PRODUCTS_WITH_CATEGORIES_SQL = `\n SELECT p.*,\n GROUP_CONCAT(c.name) as category_names,\n GROUP_CONCAT(c.slug) as category_slugs,\n GROUP_CONCAT(c.id) as category_ids\n FROM products p\n LEFT JOIN product_category_relations pcr ON p.id = pcr.product_id\n LEFT JOIN product_categories c ON pcr.category_id = c.id\n WHERE p.published = 1\n GROUP BY p.id\n`;\n\nexport function useCategories() {\n const { data, isLoading: loading, error } = useRepositoryQuery<Category>('product_categories', {\n orderBy: [{ field: 'name', direction: 'ASC' }]\n });\n\n return {\n categories: data ?? [],\n loading,\n error: error?.message ?? null\n };\n}\n\nexport function useProducts() {\n const sql = `${PRODUCTS_WITH_CATEGORIES_SQL} ORDER BY p.name`;\n\n const { data, isLoading: loading, error } = useRawQuery<any>(\n ['products', 'all'],\n sql\n );\n\n const products = useMemo(() => {\n if (!data) return [];\n return data.map(transformProduct);\n }, [data]);\n\n return { products, loading, error: error?.message ?? null };\n}\n\nexport function useProductBySlug(slug: string) {\n const sql = `\n SELECT p.*,\n GROUP_CONCAT(c.name) as category_names,\n GROUP_CONCAT(c.slug) as category_slugs,\n GROUP_CONCAT(c.id) as category_ids\n FROM products p\n LEFT JOIN product_category_relations pcr ON p.id = pcr.product_id\n LEFT JOIN product_categories c ON pcr.category_id = c.id\n WHERE p.slug = ? AND p.published = 1\n GROUP BY p.id\n `;\n\n const { data, isLoading: loading, error } = useRawQueryOne<any>(\n ['products', 'slug', slug],\n sql,\n [slug],\n { enabled: !!slug }\n );\n\n const product = useMemo(() => {\n if (!data) return null;\n return transformProduct(data);\n }, [data]);\n\n return {\n product,\n loading,\n error: !data && !loading && slug ? 'Product not found' : (error?.message ?? null)\n };\n}\n\nexport function useFeaturedProducts() {\n const sql = `\n SELECT p.*,\n GROUP_CONCAT(c.name) as category_names,\n GROUP_CONCAT(c.slug) as category_slugs,\n GROUP_CONCAT(c.id) as category_ids\n FROM products p\n LEFT JOIN product_category_relations pcr ON p.id = pcr.product_id\n LEFT JOIN product_categories c ON pcr.category_id = c.id\n WHERE p.published = 1 AND p.featured = 1\n GROUP BY p.id\n ORDER BY p.created_at DESC LIMIT 8\n `;\n\n const { data, isLoading: loading, error } = useRawQuery<any>(\n ['products', 'featured'],\n sql\n );\n\n const products = useMemo(() => {\n if (!data) return [];\n return data.map(transformProduct);\n }, [data]);\n\n return { products, loading, error: error?.message ?? null };\n}\n"
|
|
40
|
+
"content": "import { useMemo } from 'react';\r\nimport type { Product, Category, ProductCategory } from './types';\r\nimport {\r\n useRepositoryQuery,\r\n useRawQuery,\r\n useRawQueryOne,\r\n parseStringToArray,\r\n parseJSONString,\r\n parseSQLiteBoolean,\r\n parseNumberSafe\r\n} from '@/modules/db';\r\n\r\nconst transformProduct = (row: any): Product => {\r\n const categoryNames = row.category_names ? row.category_names.split(',') : [];\r\n const categorySlugs = row.category_slugs ? row.category_slugs.split(',') : [];\r\n const categoryIds = row.category_ids ? row.category_ids.split(',').map(Number) : [];\r\n\r\n const categories: ProductCategory[] = categoryIds.map((id: number, index: number) => ({\r\n id,\r\n name: categoryNames[index] || '',\r\n slug: categorySlugs[index] || '',\r\n is_primary: index === 0\r\n }));\r\n\r\n const primaryCategory = categories.length > 0 ? categories[0] : null;\r\n\r\n return {\r\n id: parseNumberSafe(row.id),\r\n name: String(row.name || ''),\r\n slug: String(row.slug || ''),\r\n description: row.description || '',\r\n price: parseNumberSafe(row.price),\r\n sale_price: row.sale_price ? parseNumberSafe(row.sale_price) : undefined,\r\n on_sale: parseSQLiteBoolean(row.on_sale),\r\n images: parseStringToArray(row.images),\r\n brand: row.brand || '',\r\n sku: row.sku || '',\r\n stock: parseNumberSafe(row.stock),\r\n tags: parseJSONString(row.tags, []) || [],\r\n rating: parseNumberSafe(row.rating),\r\n review_count: parseNumberSafe(row.review_count),\r\n featured: parseSQLiteBoolean(row.featured),\r\n is_new: parseSQLiteBoolean(row.is_new),\r\n published: parseSQLiteBoolean(row.published),\r\n specifications: parseJSONString(row.specifications, {}) || {},\r\n variants: parseJSONString(row.variants, []) || [],\r\n created_at: row.created_at || new Date().toISOString(),\r\n updated_at: row.updated_at || new Date().toISOString(),\r\n meta_description: row.meta_description || '',\r\n meta_keywords: row.meta_keywords || '',\r\n category: primaryCategory?.slug || '',\r\n category_name: primaryCategory?.name || '',\r\n categories\r\n };\r\n};\r\n\r\nconst PRODUCTS_WITH_CATEGORIES_SQL = `\r\n SELECT p.*,\r\n GROUP_CONCAT(c.name) as category_names,\r\n GROUP_CONCAT(c.slug) as category_slugs,\r\n GROUP_CONCAT(c.id) as category_ids\r\n FROM products p\r\n LEFT JOIN product_category_relations pcr ON p.id = pcr.product_id\r\n LEFT JOIN product_categories c ON pcr.category_id = c.id\r\n WHERE p.published = 1\r\n GROUP BY p.id\r\n`;\r\n\r\nexport function useCategories() {\r\n const { data, isLoading: loading, error } = useRepositoryQuery<Category>('product_categories', {\r\n orderBy: [{ field: 'name', direction: 'ASC' }]\r\n });\r\n\r\n return {\r\n categories: data ?? [],\r\n loading,\r\n error: error?.message ?? null\r\n };\r\n}\r\n\r\nexport function useProducts() {\r\n const sql = `${PRODUCTS_WITH_CATEGORIES_SQL} ORDER BY p.name`;\r\n\r\n const { data, isLoading: loading, error } = useRawQuery<any>(\r\n ['products', 'all'],\r\n sql\r\n );\r\n\r\n const products = useMemo(() => {\r\n if (!data) return [];\r\n return data.map(transformProduct);\r\n }, [data]);\r\n\r\n return { products, loading, error: error?.message ?? null };\r\n}\r\n\r\nexport function useProductBySlug(slug: string) {\r\n const sql = `\r\n SELECT p.*,\r\n GROUP_CONCAT(c.name) as category_names,\r\n GROUP_CONCAT(c.slug) as category_slugs,\r\n GROUP_CONCAT(c.id) as category_ids\r\n FROM products p\r\n LEFT JOIN product_category_relations pcr ON p.id = pcr.product_id\r\n LEFT JOIN product_categories c ON pcr.category_id = c.id\r\n WHERE p.slug = ? AND p.published = 1\r\n GROUP BY p.id\r\n `;\r\n\r\n const { data, isLoading: loading, error } = useRawQueryOne<any>(\r\n ['products', 'slug', slug],\r\n sql,\r\n [slug],\r\n { enabled: !!slug }\r\n );\r\n\r\n const product = useMemo(() => {\r\n if (!data) return null;\r\n return transformProduct(data);\r\n }, [data]);\r\n\r\n return {\r\n product,\r\n loading,\r\n error: !data && !loading && slug ? 'Product not found' : (error?.message ?? null)\r\n };\r\n}\r\n\r\nexport function useFeaturedProducts() {\r\n const sql = `\r\n SELECT p.*,\r\n GROUP_CONCAT(c.name) as category_names,\r\n GROUP_CONCAT(c.slug) as category_slugs,\r\n GROUP_CONCAT(c.id) as category_ids\r\n FROM products p\r\n LEFT JOIN product_category_relations pcr ON p.id = pcr.product_id\r\n LEFT JOIN product_categories c ON pcr.category_id = c.id\r\n WHERE p.published = 1 AND p.featured = 1\r\n GROUP BY p.id\r\n ORDER BY p.created_at DESC LIMIT 8\r\n `;\r\n\r\n const { data, isLoading: loading, error } = useRawQuery<any>(\r\n ['products', 'featured'],\r\n sql\r\n );\r\n\r\n const products = useMemo(() => {\r\n if (!data) return [];\r\n return data.map(transformProduct);\r\n }, [data]);\r\n\r\n return { products, loading, error: error?.message ?? null };\r\n}\r\n"
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"path": "ecommerce-core/useSearch.ts",
|
|
44
44
|
"type": "registry:hook",
|
|
45
45
|
"target": "$modules$/ecommerce-core/useSearch.ts",
|
|
46
|
-
"content": "import { useState, useEffect, useCallback } from 'react';\nimport type { Product } from './types';\nimport { useProducts } from './useProducts';\n\nexport const useSearch = () => {\n const [searchTerm, setSearchTerm] = useState('');\n const [results, setResults] = useState<Product[]>([]);\n const [isSearching, setIsSearching] = useState(false);\n\n // Load all products via useProducts hook\n const { products: allProducts } = useProducts();\n\n // Perform search when searchTerm changes\n useEffect(() => {\n if (!searchTerm.trim()) {\n setResults([]);\n setIsSearching(false);\n return;\n }\n\n setIsSearching(true);\n\n const searchTimeout = setTimeout(() => {\n const filtered = allProducts.filter(product => {\n const term = searchTerm.toLowerCase();\n \n // Search in product name\n if (product.name.toLowerCase().includes(term)) return true;\n \n // Search in description\n if (product.description.toLowerCase().includes(term)) return true;\n \n // Search in category\n if (product.category_name?.toLowerCase().includes(term)) return true;\n \n // Search in brand\n if (product.brand?.toLowerCase().includes(term)) return true;\n \n // Search in tags\n if (product.tags.some(tag => tag.toLowerCase().includes(term))) return true;\n \n return false;\n });\n\n setResults(filtered);\n setIsSearching(false);\n }, 300); // Debounce search\n\n return () => clearTimeout(searchTimeout);\n }, [searchTerm, allProducts]);\n\n const clearSearch = useCallback(() => {\n setSearchTerm('');\n setResults([]);\n setIsSearching(false);\n }, []);\n\n // search function that takes a term and sets the searchTerm\n const search = useCallback((term: string) => {\n setSearchTerm(term);\n }, []);\n\n return {\n searchTerm,\n setSearchTerm,\n results,\n isSearching,\n clearSearch,\n clearResults: clearSearch, // alias for header-ecommerce compatibility\n search, // function to trigger search\n hasResults: results.length > 0\n };\n};\n"
|
|
46
|
+
"content": "import { useState, useEffect, useCallback } from 'react';\r\nimport type { Product } from './types';\r\nimport { useProducts } from './useProducts';\r\n\r\nexport const useSearch = () => {\r\n const [searchTerm, setSearchTerm] = useState('');\r\n const [results, setResults] = useState<Product[]>([]);\r\n const [isSearching, setIsSearching] = useState(false);\r\n\r\n // Load all products via useProducts hook\r\n const { products: allProducts } = useProducts();\r\n\r\n // Perform search when searchTerm changes\r\n useEffect(() => {\r\n if (!searchTerm.trim()) {\r\n setResults([]);\r\n setIsSearching(false);\r\n return;\r\n }\r\n\r\n setIsSearching(true);\r\n\r\n const searchTimeout = setTimeout(() => {\r\n const filtered = allProducts.filter(product => {\r\n const term = searchTerm.toLowerCase();\r\n \r\n // Search in product name\r\n if (product.name.toLowerCase().includes(term)) return true;\r\n \r\n // Search in description\r\n if (product.description.toLowerCase().includes(term)) return true;\r\n \r\n // Search in category\r\n if (product.category_name?.toLowerCase().includes(term)) return true;\r\n \r\n // Search in brand\r\n if (product.brand?.toLowerCase().includes(term)) return true;\r\n \r\n // Search in tags\r\n if (product.tags.some(tag => tag.toLowerCase().includes(term))) return true;\r\n \r\n return false;\r\n });\r\n\r\n setResults(filtered);\r\n setIsSearching(false);\r\n }, 300); // Debounce search\r\n\r\n return () => clearTimeout(searchTimeout);\r\n }, [searchTerm, allProducts]);\r\n\r\n const clearSearch = useCallback(() => {\r\n setSearchTerm('');\r\n setResults([]);\r\n setIsSearching(false);\r\n }, []);\r\n\r\n // search function that takes a term and sets the searchTerm\r\n const search = useCallback((term: string) => {\r\n setSearchTerm(term);\r\n }, []);\r\n\r\n return {\r\n searchTerm,\r\n setSearchTerm,\r\n results,\r\n isSearching,\r\n clearSearch,\r\n clearResults: clearSearch, // alias for header-ecommerce compatibility\r\n search, // function to trigger search\r\n hasResults: results.length > 0\r\n };\r\n};\r\n"
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
49
|
"path": "ecommerce-core/format-price.ts",
|
|
50
50
|
"type": "registry:lib",
|
|
51
51
|
"target": "$modules$/ecommerce-core/format-price.ts",
|
|
52
|
-
"content": "export function formatPrice(price: number, currency: string = \"USD\"): string {\n return new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: currency,\n }).format(price);\n}\n"
|
|
52
|
+
"content": "export function formatPrice(price: number, currency: string = \"USD\"): string {\r\n return new Intl.NumberFormat(\"en-US\", {\r\n style: \"currency\",\r\n currency: currency,\r\n }).format(price);\r\n}\r\n"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"path": "ecommerce-core/payment-config.ts",
|
|
56
56
|
"type": "registry:lib",
|
|
57
57
|
"target": "$modules$/ecommerce-core/payment-config.ts",
|
|
58
|
-
"content": "// Payment configuration utility\n// Parses environment variables and provides payment method configuration\n\nexport type PaymentMethod = \"card\" | \"transfer\" | \"cash\";\nexport type OnlinePaymentProvider = \"stripe\" | \"iyzico\";\n\nexport interface PaymentMethodConfig {\n id: PaymentMethod;\n label: string;\n description: string;\n icon: string; // Icon component name from lucide-react\n requiresOnlineProvider?: boolean;\n}\n\n// Parse comma-separated env variable into array\nconst parseEnvArray = (envVar: string | undefined): string[] => {\n if (!envVar) return [];\n return envVar.split(\",\").map((item) => item.trim()).filter(Boolean);\n};\n\n// Get available payment methods from environment\nexport const getAvailablePaymentMethods = (): PaymentMethod[] => {\n const methods = parseEnvArray(import.meta.env.VITE_AVAILABLE_PAYMENT_METHODS);\n if (methods.length === 0) {\n // Default to all methods if not configured\n return [\"card\", \"transfer\", \"cash\"];\n }\n return methods as PaymentMethod[];\n};\n\n// Get available online payment providers from environment\nexport const getOnlinePaymentProviders = (): OnlinePaymentProvider[] => {\n const providers = parseEnvArray(import.meta.env.VITE_ONLINE_PAYMENT_METHODS);\n if (providers.length === 0) {\n // Default to stripe if not configured\n return [\"stripe\"];\n }\n return providers as OnlinePaymentProvider[];\n};\n\n// Payment method configurations with display information\nexport const PAYMENT_METHOD_CONFIGS: Record<PaymentMethod, PaymentMethodConfig> = {\n card: {\n id: \"card\",\n label: \"Credit/Debit Card\",\n description: \"Pay securely with your credit or debit card\",\n icon: \"CreditCard\",\n requiresOnlineProvider: true,\n },\n transfer: {\n id: \"transfer\",\n label: \"Bank Transfer\",\n description: \"Transfer payment to our bank account\",\n icon: \"Banknote\",\n },\n cash: {\n id: \"cash\",\n label: \"Cash on Delivery\",\n description: \"Pay when your order arrives at your doorstep\",\n icon: \"Truck\",\n },\n};\n\n// Online payment provider configurations\nexport const ONLINE_PROVIDER_CONFIGS: Record<OnlinePaymentProvider, { label: string; description: string }> = {\n stripe: {\n label: \"Stripe\",\n description: \"Secure payment processing by Stripe\",\n },\n iyzico: {\n label: \"iyzico\",\n description: \"Secure payment processing by iyzico\",\n },\n};\n\n// Check if a payment method is available\nexport const isPaymentMethodAvailable = (method: PaymentMethod): boolean => {\n const available = getAvailablePaymentMethods();\n return available.includes(method);\n};\n\n// Check if an online provider is available\nexport const isOnlineProviderAvailable = (provider: OnlinePaymentProvider): boolean => {\n const available = getOnlinePaymentProviders();\n return available.includes(provider);\n};\n\n// Get filtered payment method configs (only available ones)\nexport const getFilteredPaymentMethodConfigs = (): PaymentMethodConfig[] => {\n const availableMethods = getAvailablePaymentMethods();\n return availableMethods\n .map((method) => PAYMENT_METHOD_CONFIGS[method])\n .filter(Boolean);\n};\n"
|
|
58
|
+
"content": "// Payment configuration utility\r\n// Parses environment variables and provides payment method configuration\r\n\r\nexport type PaymentMethod = \"card\" | \"transfer\" | \"cash\";\r\nexport type OnlinePaymentProvider = \"stripe\" | \"iyzico\";\r\n\r\nexport interface PaymentMethodConfig {\r\n id: PaymentMethod;\r\n label: string;\r\n description: string;\r\n icon: string; // Icon component name from lucide-react\r\n requiresOnlineProvider?: boolean;\r\n}\r\n\r\n// Parse comma-separated env variable into array\r\nconst parseEnvArray = (envVar: string | undefined): string[] => {\r\n if (!envVar) return [];\r\n return envVar.split(\",\").map((item) => item.trim()).filter(Boolean);\r\n};\r\n\r\n// Get available payment methods from environment\r\nexport const getAvailablePaymentMethods = (): PaymentMethod[] => {\r\n const methods = parseEnvArray(import.meta.env.VITE_AVAILABLE_PAYMENT_METHODS);\r\n if (methods.length === 0) {\r\n // Default to all methods if not configured\r\n return [\"card\", \"transfer\", \"cash\"];\r\n }\r\n return methods as PaymentMethod[];\r\n};\r\n\r\n// Get available online payment providers from environment\r\nexport const getOnlinePaymentProviders = (): OnlinePaymentProvider[] => {\r\n const providers = parseEnvArray(import.meta.env.VITE_ONLINE_PAYMENT_METHODS);\r\n if (providers.length === 0) {\r\n // Default to stripe if not configured\r\n return [\"stripe\"];\r\n }\r\n return providers as OnlinePaymentProvider[];\r\n};\r\n\r\n// Payment method configurations with display information\r\nexport const PAYMENT_METHOD_CONFIGS: Record<PaymentMethod, PaymentMethodConfig> = {\r\n card: {\r\n id: \"card\",\r\n label: \"Credit/Debit Card\",\r\n description: \"Pay securely with your credit or debit card\",\r\n icon: \"CreditCard\",\r\n requiresOnlineProvider: true,\r\n },\r\n transfer: {\r\n id: \"transfer\",\r\n label: \"Bank Transfer\",\r\n description: \"Transfer payment to our bank account\",\r\n icon: \"Banknote\",\r\n },\r\n cash: {\r\n id: \"cash\",\r\n label: \"Cash on Delivery\",\r\n description: \"Pay when your order arrives at your doorstep\",\r\n icon: \"Truck\",\r\n },\r\n};\r\n\r\n// Online payment provider configurations\r\nexport const ONLINE_PROVIDER_CONFIGS: Record<OnlinePaymentProvider, { label: string; description: string }> = {\r\n stripe: {\r\n label: \"Stripe\",\r\n description: \"Secure payment processing by Stripe\",\r\n },\r\n iyzico: {\r\n label: \"iyzico\",\r\n description: \"Secure payment processing by iyzico\",\r\n },\r\n};\r\n\r\n// Check if a payment method is available\r\nexport const isPaymentMethodAvailable = (method: PaymentMethod): boolean => {\r\n const available = getAvailablePaymentMethods();\r\n return available.includes(method);\r\n};\r\n\r\n// Check if an online provider is available\r\nexport const isOnlineProviderAvailable = (provider: OnlinePaymentProvider): boolean => {\r\n const available = getOnlinePaymentProviders();\r\n return available.includes(provider);\r\n};\r\n\r\n// Get filtered payment method configs (only available ones)\r\nexport const getFilteredPaymentMethodConfigs = (): PaymentMethodConfig[] => {\r\n const availableMethods = getAvailablePaymentMethods();\r\n return availableMethods\r\n .map((method) => PAYMENT_METHOD_CONFIGS[method])\r\n .filter(Boolean);\r\n};\r\n"
|
|
59
59
|
},
|
|
60
60
|
{
|
|
61
61
|
"path": "ecommerce-core/lang/en.json",
|
|
62
62
|
"type": "registry:lang",
|
|
63
63
|
"target": "$modules$/ecommerce-core/lang/en.json",
|
|
64
|
-
"content": "{\n \"cart\": \"Cart\",\n \"addToCart\": \"Add to Cart\",\n \"removeFromCart\": \"Remove\",\n \"clearCart\": \"Clear Cart\",\n \"emptyCart\": \"Your cart is empty\",\n \"cartTotal\": \"Total\",\n \"checkout\": \"Checkout\",\n \"quantity\": \"Quantity\",\n \"favorites\": \"Favorites\",\n \"addToFavorites\": \"Add to Favorites\",\n \"removeFromFavorites\": \"Remove from Favorites\",\n \"noFavorites\": \"No favorites yet\",\n \"products\": \"Products\",\n \"search\": \"Search\",\n \"searchPlaceholder\": \"Search products...\",\n \"noResults\": \"No products found\",\n \"outOfStock\": \"Out of Stock\",\n \"inStock\": \"In Stock\",\n \"sale\": \"Sale\",\n \"new\": \"New\",\n \"featured\": \"Featured\"\n}\n"
|
|
64
|
+
"content": "{\r\n \"cart\": \"Cart\",\r\n \"addToCart\": \"Add to Cart\",\r\n \"removeFromCart\": \"Remove\",\r\n \"clearCart\": \"Clear Cart\",\r\n \"emptyCart\": \"Your cart is empty\",\r\n \"cartTotal\": \"Total\",\r\n \"checkout\": \"Checkout\",\r\n \"quantity\": \"Quantity\",\r\n \"favorites\": \"Favorites\",\r\n \"addToFavorites\": \"Add to Favorites\",\r\n \"removeFromFavorites\": \"Remove from Favorites\",\r\n \"noFavorites\": \"No favorites yet\",\r\n \"products\": \"Products\",\r\n \"search\": \"Search\",\r\n \"searchPlaceholder\": \"Search products...\",\r\n \"noResults\": \"No products found\",\r\n \"outOfStock\": \"Out of Stock\",\r\n \"inStock\": \"In Stock\",\r\n \"sale\": \"Sale\",\r\n \"new\": \"New\",\r\n \"featured\": \"Featured\"\r\n}\r\n"
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
"path": "ecommerce-core/lang/tr.json",
|
|
68
68
|
"type": "registry:lang",
|
|
69
69
|
"target": "$modules$/ecommerce-core/lang/tr.json",
|
|
70
|
-
"content": "{\n \"cart\": \"Sepet\",\n \"addToCart\": \"Sepete Ekle\",\n \"removeFromCart\": \"Kaldır\",\n \"clearCart\": \"Sepeti Temizle\",\n \"emptyCart\": \"Sepetiniz boş\",\n \"cartTotal\": \"Toplam\",\n \"checkout\": \"Ödeme\",\n \"quantity\": \"Adet\",\n \"favorites\": \"Favoriler\",\n \"addToFavorites\": \"Favorilere Ekle\",\n \"removeFromFavorites\": \"Favorilerden Kaldır\",\n \"noFavorites\": \"Henüz favori yok\",\n \"products\": \"Ürünler\",\n \"search\": \"Ara\",\n \"searchPlaceholder\": \"Ürün ara...\",\n \"noResults\": \"Ürün bulunamadı\",\n \"outOfStock\": \"Stokta Yok\",\n \"inStock\": \"Stokta\",\n \"sale\": \"İndirim\",\n \"new\": \"Yeni\",\n \"featured\": \"Öne Çıkan\"\n}\n"
|
|
70
|
+
"content": "{\r\n \"cart\": \"Sepet\",\r\n \"addToCart\": \"Sepete Ekle\",\r\n \"removeFromCart\": \"Kaldır\",\r\n \"clearCart\": \"Sepeti Temizle\",\r\n \"emptyCart\": \"Sepetiniz boş\",\r\n \"cartTotal\": \"Toplam\",\r\n \"checkout\": \"Ödeme\",\r\n \"quantity\": \"Adet\",\r\n \"favorites\": \"Favoriler\",\r\n \"addToFavorites\": \"Favorilere Ekle\",\r\n \"removeFromFavorites\": \"Favorilerden Kaldır\",\r\n \"noFavorites\": \"Henüz favori yok\",\r\n \"products\": \"Ürünler\",\r\n \"search\": \"Ara\",\r\n \"searchPlaceholder\": \"Ürün ara...\",\r\n \"noResults\": \"Ürün bulunamadı\",\r\n \"outOfStock\": \"Stokta Yok\",\r\n \"inStock\": \"Stokta\",\r\n \"sale\": \"İndirim\",\r\n \"new\": \"Yeni\",\r\n \"featured\": \"Öne Çıkan\"\r\n}\r\n"
|
|
71
71
|
}
|
|
72
72
|
],
|
|
73
73
|
"exports": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"path": "empty-page/index.ts",
|
|
15
15
|
"type": "registry:index",
|
|
16
16
|
"target": "$modules$/empty-page/index.ts",
|
|
17
|
-
"content": "export * from './empty-page';\nexport { EmptyPage as default } from './empty-page';\n"
|
|
17
|
+
"content": "export * from './empty-page';\r\nexport { EmptyPage as default } from './empty-page';\r\n"
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
"path": "empty-page/empty-page.tsx",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
"path": "empty-page/lang/en.json",
|
|
27
27
|
"type": "registry:lang",
|
|
28
28
|
"target": "$modules$/empty-page/lang/en.json",
|
|
29
|
-
"content": "{\n \"title\": \"Page Title\",\n \"heading\": \"Page Heading\",\n \"description\": \"This is an empty page template. Replace this content with your own.\"\n}\n"
|
|
29
|
+
"content": "{\r\n \"title\": \"Page Title\",\r\n \"heading\": \"Page Heading\",\r\n \"description\": \"This is an empty page template. Replace this content with your own.\"\r\n}\r\n"
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
"path": "empty-page/lang/tr.json",
|
|
33
33
|
"type": "registry:lang",
|
|
34
34
|
"target": "$modules$/empty-page/lang/tr.json",
|
|
35
|
-
"content": "{\n \"title\": \"Sayfa Başlığı\",\n \"heading\": \"Sayfa Başlığı\",\n \"description\": \"Bu boş bir sayfa şablonudur. Bu içeriği kendi içeriğinizle değiştirin.\"\n}\n"
|
|
35
|
+
"content": "{\r\n \"title\": \"Sayfa Başlığı\",\r\n \"heading\": \"Sayfa Başlığı\",\r\n \"description\": \"Bu boş bir sayfa şablonudur. Bu içeriği kendi içeriğinizle değiştirin.\"\r\n}\r\n"
|
|
36
36
|
}
|
|
37
37
|
],
|
|
38
38
|
"exports": {
|
|
@@ -12,25 +12,25 @@
|
|
|
12
12
|
"path": "faq-categorized/index.ts",
|
|
13
13
|
"type": "registry:index",
|
|
14
14
|
"target": "$modules$/faq-categorized/index.ts",
|
|
15
|
-
"content": "export * from './faq-categorized';\n"
|
|
15
|
+
"content": "export * from './faq-categorized';\r\n"
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"path": "faq-categorized/faq-categorized.tsx",
|
|
19
19
|
"type": "registry:component",
|
|
20
20
|
"target": "$modules$/faq-categorized/faq-categorized.tsx",
|
|
21
|
-
"content": "import { useTranslation } from \"react-i18next\";\nimport { cn } from \"@/lib/utils\";\nimport {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from \"@/components/ui/accordion\";\n\ninterface FaqCategorizedProps {\n className?: string;\n}\n\nexport function FaqCategorized({ className }: FaqCategorizedProps) {\n const { t } = useTranslation(\"faq-categorized\");\n\n const categories = [\n {\n title: t(\"generalTitle\", \"General\"),\n items: [\n {\n question: t(\"general1Q\", \"What is this platform?\"),\n answer: t(\"general1A\", \"Our platform is a comprehensive solution for building modern web applications. It provides tools, components, and infrastructure to help you ship faster.\"),\n },\n {\n question: t(\"general2Q\", \"How do I get started?\"),\n answer: t(\"general2A\", \"Getting started is easy! Sign up for a free account, follow our quick start guide, and you'll be up and running in minutes.\"),\n },\n {\n question: t(\"general3Q\", \"Is there a free trial?\"),\n answer: t(\"general3A\", \"Yes, we offer a 14-day free trial with full access to all features. No credit card required to start.\"),\n },\n ],\n },\n {\n title: t(\"billingTitle\", \"Billing\"),\n items: [\n {\n question: t(\"billing1Q\", \"What payment methods do you accept?\"),\n answer: t(\"billing1A\", \"We accept all major credit cards (Visa, MasterCard, American Express), PayPal, and bank transfers for annual plans.\"),\n },\n {\n question: t(\"billing2Q\", \"Can I change my plan later?\"),\n answer: t(\"billing2A\", \"Absolutely! You can upgrade or downgrade your plan at any time. Changes take effect immediately, and we'll prorate the difference.\"),\n },\n {\n question: t(\"billing3Q\", \"Do you offer refunds?\"),\n answer: t(\"billing3A\", \"Yes, we offer a 30-day money-back guarantee. If you're not satisfied within the first 30 days, we'll issue a full refund.\"),\n },\n ],\n },\n {\n title: t(\"technicalTitle\", \"Technical\"),\n items: [\n {\n question: t(\"technical1Q\", \"What technologies do you support?\"),\n answer: t(\"technical1A\", \"We support React, Vue, Angular, and vanilla JavaScript. Our APIs are RESTful and work with any backend technology.\"),\n },\n {\n question: t(\"technical2Q\", \"Is there an API available?\"),\n answer: t(\"technical2A\", \"Yes, we provide a comprehensive REST API with detailed documentation, SDKs for popular languages, and webhook support.\"),\n },\n {\n question: t(\"technical3Q\", \"How is my data protected?\"),\n answer: t(\"technical3A\", \"We use industry-standard encryption (AES-256 at rest, TLS 1.3 in transit), regular security audits, and comply with SOC 2 and GDPR requirements.\"),\n },\n ],\n },\n ];\n\n return (\n <section className={cn(\"py-16 md:py-24\", className)}>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\n <div className=\"text-center mb-12\">\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\n {t(\"title\", \"Frequently Asked Questions\")}\n </h2>\n <p className=\"text-muted-foreground max-w-2xl mx-auto\">\n {t(\"subtitle\", \"Browse through our categorized FAQ to find answers to your questions.\")}\n </p>\n </div>\n\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto\">\n {categories.map((category, categoryIndex) => (\n <div key={categoryIndex}>\n <h3 className=\"font-semibold text-lg mb-4 text-primary\">\n {category.title}\n </h3>\n <Accordion type=\"single\" collapsible className=\"w-full\">\n {category.items.map((item, itemIndex) => (\n <AccordionItem\n key={itemIndex}\n value={`${categoryIndex}-${itemIndex}`}\n >\n <AccordionTrigger className=\"text-left text-sm font-medium hover:no-underline\">\n {item.question}\n </AccordionTrigger>\n <AccordionContent className=\"text-sm text-muted-foreground\">\n {item.answer}\n </AccordionContent>\n </AccordionItem>\n ))}\n </Accordion>\n </div>\n ))}\n </div>\n </div>\n </section>\n );\n}\n"
|
|
21
|
+
"content": "import { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport {\r\n Accordion,\r\n AccordionContent,\r\n AccordionItem,\r\n AccordionTrigger,\r\n} from \"@/components/ui/accordion\";\r\n\r\ninterface FaqCategorizedProps {\r\n className?: string;\r\n}\r\n\r\nexport function FaqCategorized({ className }: FaqCategorizedProps) {\r\n const { t } = useTranslation(\"faq-categorized\");\r\n\r\n const categories = [\r\n {\r\n title: t(\"generalTitle\", \"General\"),\r\n items: [\r\n {\r\n question: t(\"general1Q\", \"What is this platform?\"),\r\n answer: t(\"general1A\", \"Our platform is a comprehensive solution for building modern web applications. It provides tools, components, and infrastructure to help you ship faster.\"),\r\n },\r\n {\r\n question: t(\"general2Q\", \"How do I get started?\"),\r\n answer: t(\"general2A\", \"Getting started is easy! Sign up for a free account, follow our quick start guide, and you'll be up and running in minutes.\"),\r\n },\r\n {\r\n question: t(\"general3Q\", \"Is there a free trial?\"),\r\n answer: t(\"general3A\", \"Yes, we offer a 14-day free trial with full access to all features. No credit card required to start.\"),\r\n },\r\n ],\r\n },\r\n {\r\n title: t(\"billingTitle\", \"Billing\"),\r\n items: [\r\n {\r\n question: t(\"billing1Q\", \"What payment methods do you accept?\"),\r\n answer: t(\"billing1A\", \"We accept all major credit cards (Visa, MasterCard, American Express), PayPal, and bank transfers for annual plans.\"),\r\n },\r\n {\r\n question: t(\"billing2Q\", \"Can I change my plan later?\"),\r\n answer: t(\"billing2A\", \"Absolutely! You can upgrade or downgrade your plan at any time. Changes take effect immediately, and we'll prorate the difference.\"),\r\n },\r\n {\r\n question: t(\"billing3Q\", \"Do you offer refunds?\"),\r\n answer: t(\"billing3A\", \"Yes, we offer a 30-day money-back guarantee. If you're not satisfied within the first 30 days, we'll issue a full refund.\"),\r\n },\r\n ],\r\n },\r\n {\r\n title: t(\"technicalTitle\", \"Technical\"),\r\n items: [\r\n {\r\n question: t(\"technical1Q\", \"What technologies do you support?\"),\r\n answer: t(\"technical1A\", \"We support React, Vue, Angular, and vanilla JavaScript. Our APIs are RESTful and work with any backend technology.\"),\r\n },\r\n {\r\n question: t(\"technical2Q\", \"Is there an API available?\"),\r\n answer: t(\"technical2A\", \"Yes, we provide a comprehensive REST API with detailed documentation, SDKs for popular languages, and webhook support.\"),\r\n },\r\n {\r\n question: t(\"technical3Q\", \"How is my data protected?\"),\r\n answer: t(\"technical3A\", \"We use industry-standard encryption (AES-256 at rest, TLS 1.3 in transit), regular security audits, and comply with SOC 2 and GDPR requirements.\"),\r\n },\r\n ],\r\n },\r\n ];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n <div className=\"text-center mb-12\">\r\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\r\n {t(\"title\", \"Frequently Asked Questions\")}\r\n </h2>\r\n <p className=\"text-muted-foreground max-w-2xl mx-auto\">\r\n {t(\"subtitle\", \"Browse through our categorized FAQ to find answers to your questions.\")}\r\n </p>\r\n </div>\r\n\r\n <div className=\"grid md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto\">\r\n {categories.map((category, categoryIndex) => (\r\n <div key={categoryIndex}>\r\n <h3 className=\"font-semibold text-lg mb-4 text-primary\">\r\n {category.title}\r\n </h3>\r\n <Accordion type=\"single\" collapsible className=\"w-full\">\r\n {category.items.map((item, itemIndex) => (\r\n <AccordionItem\r\n key={itemIndex}\r\n value={`${categoryIndex}-${itemIndex}`}\r\n >\r\n <AccordionTrigger className=\"text-left text-sm font-medium hover:no-underline\">\r\n {item.question}\r\n </AccordionTrigger>\r\n <AccordionContent className=\"text-sm text-muted-foreground\">\r\n {item.answer}\r\n </AccordionContent>\r\n </AccordionItem>\r\n ))}\r\n </Accordion>\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
"path": "faq-categorized/lang/en.json",
|
|
25
25
|
"type": "registry:lang",
|
|
26
26
|
"target": "$modules$/faq-categorized/lang/en.json",
|
|
27
|
-
"content": "{\n \"title\": \"Frequently Asked Questions\",\n \"subtitle\": \"This introductory text helps visitors navigate your categorized FAQs. Explain how the categories are organized, encourage visitors to explore relevant sections, or mention that you're available for additional questions. Use Promake to create a helpful guide to your FAQ structure.\",\n \"generalTitle\": \"General\",\n \"general1Q\": \"Replace this with your first general question\",\n \"general1A\": \"Provide a comprehensive answer to this general question. Include key information that helps visitors understand your offering at a high level. Use Promake to customize with specific details that address common initial questions about what you do and how it works.\",\n \"general2Q\": \"This is a placeholder question that needs customization\",\n \"general2A\": \"Give a clear, informative answer that provides value to visitors with general questions. Be thorough but accessible. Use Promake to add information that helps newcomers understand your offering and its benefits.\",\n \"general3Q\": \"Customize this question for your FAQ section\",\n \"general3A\": \"Offer a detailed response that addresses this general aspect of your offering. Include relevant context and examples. Use Promake to ensure this answer helps visitors understand the fundamentals of what you provide.\",\n \"billingTitle\": \"Billing\",\n \"billing1Q\": \"Replace with your billing-related question\",\n \"billing1A\": \"Provide clear information about your billing practices, payment methods, or pricing structure. Be transparent and specific about costs, payment terms, and any relevant policies. Use Promake to add details that help customers understand your billing process.\",\n \"billing2Q\": \"This question should be replaced with real billing FAQ\",\n \"billing2A\": \"Explain billing-related policies, procedures, or options in detail. Address common concerns about payments, refunds, or invoicing. Use Promake to provide accurate information that builds trust in your billing practices.\",\n \"billing3Q\": \"Customize this billing question\",\n \"billing3A\": \"Give a thorough answer about billing or payment matters. Include important details about pricing tiers, payment schedules, or billing support. Use Promake to ensure customers have all the billing information they need.\",\n \"technicalTitle\": \"Technical\",\n \"technical1Q\": \"Replace with your technical question\",\n \"technical1A\": \"Provide technical information that helps users understand system requirements, compatibility, or implementation details. Be specific but avoid unnecessary jargon. Use Promake to add technical details that are relevant and helpful to your users.\",\n \"technical2Q\": \"This is a placeholder technical question\",\n \"technical2A\": \"Explain technical aspects in a way that's accessible to your audience. Include specific details about features, integrations, or technical capabilities. Use Promake to customize with technical information that addresses common questions.\",\n \"technical3Q\": \"Customize this final technical question\",\n \"technical3A\": \"Give a detailed technical answer that provides the specific information users need. Include relevant specifications, limitations, or technical support options. Use Promake to ensure technical questions are answered thoroughly and accurately.\"\n}"
|
|
27
|
+
"content": "{\r\n \"title\": \"Frequently Asked Questions\",\r\n \"subtitle\": \"This introductory text helps visitors navigate your categorized FAQs. Explain how the categories are organized, encourage visitors to explore relevant sections, or mention that you're available for additional questions. Use Promake to create a helpful guide to your FAQ structure.\",\r\n \"generalTitle\": \"General\",\r\n \"general1Q\": \"Replace this with your first general question\",\r\n \"general1A\": \"Provide a comprehensive answer to this general question. Include key information that helps visitors understand your offering at a high level. Use Promake to customize with specific details that address common initial questions about what you do and how it works.\",\r\n \"general2Q\": \"This is a placeholder question that needs customization\",\r\n \"general2A\": \"Give a clear, informative answer that provides value to visitors with general questions. Be thorough but accessible. Use Promake to add information that helps newcomers understand your offering and its benefits.\",\r\n \"general3Q\": \"Customize this question for your FAQ section\",\r\n \"general3A\": \"Offer a detailed response that addresses this general aspect of your offering. Include relevant context and examples. Use Promake to ensure this answer helps visitors understand the fundamentals of what you provide.\",\r\n \"billingTitle\": \"Billing\",\r\n \"billing1Q\": \"Replace with your billing-related question\",\r\n \"billing1A\": \"Provide clear information about your billing practices, payment methods, or pricing structure. Be transparent and specific about costs, payment terms, and any relevant policies. Use Promake to add details that help customers understand your billing process.\",\r\n \"billing2Q\": \"This question should be replaced with real billing FAQ\",\r\n \"billing2A\": \"Explain billing-related policies, procedures, or options in detail. Address common concerns about payments, refunds, or invoicing. Use Promake to provide accurate information that builds trust in your billing practices.\",\r\n \"billing3Q\": \"Customize this billing question\",\r\n \"billing3A\": \"Give a thorough answer about billing or payment matters. Include important details about pricing tiers, payment schedules, or billing support. Use Promake to ensure customers have all the billing information they need.\",\r\n \"technicalTitle\": \"Technical\",\r\n \"technical1Q\": \"Replace with your technical question\",\r\n \"technical1A\": \"Provide technical information that helps users understand system requirements, compatibility, or implementation details. Be specific but avoid unnecessary jargon. Use Promake to add technical details that are relevant and helpful to your users.\",\r\n \"technical2Q\": \"This is a placeholder technical question\",\r\n \"technical2A\": \"Explain technical aspects in a way that's accessible to your audience. Include specific details about features, integrations, or technical capabilities. Use Promake to customize with technical information that addresses common questions.\",\r\n \"technical3Q\": \"Customize this final technical question\",\r\n \"technical3A\": \"Give a detailed technical answer that provides the specific information users need. Include relevant specifications, limitations, or technical support options. Use Promake to ensure technical questions are answered thoroughly and accurately.\"\r\n}"
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
"path": "faq-categorized/lang/tr.json",
|
|
31
31
|
"type": "registry:lang",
|
|
32
32
|
"target": "$modules$/faq-categorized/lang/tr.json",
|
|
33
|
-
"content": "{\n \"title\": \"Sıkça Sorulan Sorular\",\n \"subtitle\": \"Bu giriş metni ziyaretçilerin kategorize edilmiş SSS'lerinizde gezinmesine yardımcı olur. Kategorilerin nasıl düzenlendiğini açıklayın, ziyaretçileri ilgili bölümleri keşfetmeye teşvik edin veya ek sorular için müsait olduğunuzdan bahsedin. Promake ile SSS yapınıza yardımcı bir rehber oluşturun.\",\n \"generalTitle\": \"Genel\",\n \"general1Q\": \"Bunu ilk genel sorunuzla değiştirin\",\n \"general1A\": \"Bu genel soruya kapsamlı bir cevap verin. Ziyaretçilerin teklifinizi genel düzeyde anlamalarına yardımcı olacak temel bilgileri ekleyin. Promake ile ne yaptığınız ve nasıl çalıştığı hakkındaki yaygın başlangıç sorularını ele alan özel detaylarla özelleştirin.\",\n \"general2Q\": \"Bu özelleştirilmesi gereken bir placeholder sorudur\",\n \"general2A\": \"Genel sorular olan ziyaretçilere değer sağlayan açık ve bilgilendirici bir cevap verin. Kapsamlı ama anlaşılır olun. Promake ile yeni gelenlerin teklifinizi ve faydalarını anlamalarına yardımcı olan bilgiler ekleyin.\",\n \"general3Q\": \"Bu soruyu SSS bölümünüz için özelleştirin\",\n \"general3A\": \"Teklifinizin bu genel yönünü ele alan detaylı bir yanıt sunun. İlgili bağlam ve örnekler ekleyin. Promake ile bu cevabın ziyaretçilerin sağladığınız şeyin temellerini anlamalarına yardımcı olduğundan emin olun.\",\n \"billingTitle\": \"Faturalama\",\n \"billing1Q\": \"Faturalama ile ilgili sorunuzla değiştirin\",\n \"billing1A\": \"Faturalama uygulamalarınız, ödeme yöntemleriniz veya fiyatlandırma yapınız hakkında açık bilgi sağlayın. Maliyetler, ödeme koşulları ve ilgili politikalar konusunda şeffaf ve spesifik olun. Promake ile müşterilerin faturalama sürecinizi anlamalarına yardımcı olan detaylar ekleyin.\",\n \"billing2Q\": \"Bu soru gerçek faturalama SSS'si ile değiştirilmelidir\",\n \"billing2A\": \"Faturalandırmayla ilgili politikaları, prosedürleri veya seçenekleri detaylı açıklayın. Ödemeler, iadeler veya faturalandırma hakkındaki yaygın endişeleri ele alın. Promake ile faturalama uygulamalarınıza güven oluşturan doğru bilgiler sağlayın.\",\n \"billing3Q\": \"Bu faturalama sorusunu özelleştirin\",\n \"billing3A\": \"Faturalama veya ödeme konuları hakkında kapsamlı bir cevap verin. Fiyatlandırma katmanları, ödeme programları veya faturalama desteği hakkında önemli detayları ekleyin. Promake ile müşterilerin ihtiyaç duydukları tüm faturalama bilgilerine sahip olduklarından emin olun.\",\n \"technicalTitle\": \"Teknik\",\n \"technical1Q\": \"Teknik sorunuzla değiştirin\",\n \"technical1A\": \"Kullanıcıların sistem gereksinimlerini, uyumluluğu veya uygulama detaylarını anlamalarına yardımcı olan teknik bilgiler sağlayın. Spesifik olun ama gereksiz jargondan kaçının. Promake ile kullanıcılarınız için alakalı ve yardımcı olan teknik detaylar ekleyin.\",\n \"technical2Q\": \"Bu bir placeholder teknik sorudur\",\n \"technical2A\": \"Teknik yönleri hedef kitlenize erişilebilir bir şekilde açıklayın. Özellikler, entegrasyonlar veya teknik yetenekler hakkında özel detaylar ekleyin. Promake ile yaygın soruları ele alan teknik bilgilerle özelleştirin.\",\n \"technical3Q\": \"Bu son teknik soruyu özelleştirin\",\n \"technical3A\": \"Kullanıcıların ihtiyaç duyduğu özel bilgiyi sağlayan detaylı bir teknik cevap verin. İlgili özellikleri, sınırlamaları veya teknik destek seçeneklerini ekleyin. Promake ile teknik soruların kapsamlı ve doğru şekilde yanıtlandığından emin olun.\"\n}"
|
|
33
|
+
"content": "{\r\n \"title\": \"Sıkça Sorulan Sorular\",\r\n \"subtitle\": \"Bu giriş metni ziyaretçilerin kategorize edilmiş SSS'lerinizde gezinmesine yardımcı olur. Kategorilerin nasıl düzenlendiğini açıklayın, ziyaretçileri ilgili bölümleri keşfetmeye teşvik edin veya ek sorular için müsait olduğunuzdan bahsedin. Promake ile SSS yapınıza yardımcı bir rehber oluşturun.\",\r\n \"generalTitle\": \"Genel\",\r\n \"general1Q\": \"Bunu ilk genel sorunuzla değiştirin\",\r\n \"general1A\": \"Bu genel soruya kapsamlı bir cevap verin. Ziyaretçilerin teklifinizi genel düzeyde anlamalarına yardımcı olacak temel bilgileri ekleyin. Promake ile ne yaptığınız ve nasıl çalıştığı hakkındaki yaygın başlangıç sorularını ele alan özel detaylarla özelleştirin.\",\r\n \"general2Q\": \"Bu özelleştirilmesi gereken bir placeholder sorudur\",\r\n \"general2A\": \"Genel sorular olan ziyaretçilere değer sağlayan açık ve bilgilendirici bir cevap verin. Kapsamlı ama anlaşılır olun. Promake ile yeni gelenlerin teklifinizi ve faydalarını anlamalarına yardımcı olan bilgiler ekleyin.\",\r\n \"general3Q\": \"Bu soruyu SSS bölümünüz için özelleştirin\",\r\n \"general3A\": \"Teklifinizin bu genel yönünü ele alan detaylı bir yanıt sunun. İlgili bağlam ve örnekler ekleyin. Promake ile bu cevabın ziyaretçilerin sağladığınız şeyin temellerini anlamalarına yardımcı olduğundan emin olun.\",\r\n \"billingTitle\": \"Faturalama\",\r\n \"billing1Q\": \"Faturalama ile ilgili sorunuzla değiştirin\",\r\n \"billing1A\": \"Faturalama uygulamalarınız, ödeme yöntemleriniz veya fiyatlandırma yapınız hakkında açık bilgi sağlayın. Maliyetler, ödeme koşulları ve ilgili politikalar konusunda şeffaf ve spesifik olun. Promake ile müşterilerin faturalama sürecinizi anlamalarına yardımcı olan detaylar ekleyin.\",\r\n \"billing2Q\": \"Bu soru gerçek faturalama SSS'si ile değiştirilmelidir\",\r\n \"billing2A\": \"Faturalandırmayla ilgili politikaları, prosedürleri veya seçenekleri detaylı açıklayın. Ödemeler, iadeler veya faturalandırma hakkındaki yaygın endişeleri ele alın. Promake ile faturalama uygulamalarınıza güven oluşturan doğru bilgiler sağlayın.\",\r\n \"billing3Q\": \"Bu faturalama sorusunu özelleştirin\",\r\n \"billing3A\": \"Faturalama veya ödeme konuları hakkında kapsamlı bir cevap verin. Fiyatlandırma katmanları, ödeme programları veya faturalama desteği hakkında önemli detayları ekleyin. Promake ile müşterilerin ihtiyaç duydukları tüm faturalama bilgilerine sahip olduklarından emin olun.\",\r\n \"technicalTitle\": \"Teknik\",\r\n \"technical1Q\": \"Teknik sorunuzla değiştirin\",\r\n \"technical1A\": \"Kullanıcıların sistem gereksinimlerini, uyumluluğu veya uygulama detaylarını anlamalarına yardımcı olan teknik bilgiler sağlayın. Spesifik olun ama gereksiz jargondan kaçının. Promake ile kullanıcılarınız için alakalı ve yardımcı olan teknik detaylar ekleyin.\",\r\n \"technical2Q\": \"Bu bir placeholder teknik sorudur\",\r\n \"technical2A\": \"Teknik yönleri hedef kitlenize erişilebilir bir şekilde açıklayın. Özellikler, entegrasyonlar veya teknik yetenekler hakkında özel detaylar ekleyin. Promake ile yaygın soruları ele alan teknik bilgilerle özelleştirin.\",\r\n \"technical3Q\": \"Bu son teknik soruyu özelleştirin\",\r\n \"technical3A\": \"Kullanıcıların ihtiyaç duyduğu özel bilgiyi sağlayan detaylı bir teknik cevap verin. İlgili özellikleri, sınırlamaları veya teknik destek seçeneklerini ekleyin. Promake ile teknik soruların kapsamlı ve doğru şekilde yanıtlandığından emin olun.\"\r\n}"
|
|
34
34
|
}
|
|
35
35
|
],
|
|
36
36
|
"exports": {
|
|
@@ -12,25 +12,25 @@
|
|
|
12
12
|
"path": "faq-simple/index.ts",
|
|
13
13
|
"type": "registry:index",
|
|
14
14
|
"target": "$modules$/faq-simple/index.ts",
|
|
15
|
-
"content": "export * from './faq-simple';\n"
|
|
15
|
+
"content": "export * from './faq-simple';\r\n"
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"path": "faq-simple/faq-simple.tsx",
|
|
19
19
|
"type": "registry:component",
|
|
20
20
|
"target": "$modules$/faq-simple/faq-simple.tsx",
|
|
21
|
-
"content": "import { useTranslation } from \"react-i18next\";\nimport { cn } from \"@/lib/utils\";\nimport {\n Accordion,\n AccordionContent,\n AccordionItem,\n AccordionTrigger,\n} from \"@/components/ui/accordion\";\n\ninterface FaqSimpleProps {\n className?: string;\n}\n\nexport function FaqSimple({ className }: FaqSimpleProps) {\n const { t } = useTranslation(\"faq-simple\");\n\n const faqItems = [\n {\n id: \"faq-1\",\n question: t(\"q1\", \"What is included in the free plan?\"),\n answer: t(\"a1\", \"The free plan includes basic features such as up to 3 projects, 1GB storage, and email support. Perfect for individuals and small teams getting started.\"),\n },\n {\n id: \"faq-2\",\n question: t(\"q2\", \"Can I upgrade or downgrade my plan?\"),\n answer: t(\"a2\", \"Yes, you can upgrade or downgrade your plan at any time. Changes will be reflected in your next billing cycle. No penalties for switching plans.\"),\n },\n {\n id: \"faq-3\",\n question: t(\"q3\", \"How do I cancel my subscription?\"),\n answer: t(\"a3\", \"You can cancel your subscription from your account settings. Your access will continue until the end of your current billing period.\"),\n },\n {\n id: \"faq-4\",\n question: t(\"q4\", \"Is my data secure?\"),\n answer: t(\"a4\", \"Yes, we take security seriously. All data is encrypted at rest and in transit. We comply with industry standards and regularly undergo security audits.\"),\n },\n {\n id: \"faq-5\",\n question: t(\"q5\", \"Do you offer customer support?\"),\n answer: t(\"a5\", \"We offer 24/7 customer support via email and live chat. Premium plans also include phone support and dedicated account managers.\"),\n },\n {\n id: \"faq-6\",\n question: t(\"q6\", \"Can I get a refund?\"),\n answer: t(\"a6\", \"We offer a 30-day money-back guarantee for all paid plans. If you're not satisfied, contact our support team for a full refund.\"),\n },\n ];\n\n return (\n <section className={cn(\"py-16 md:py-24\", className)}>\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 max-w-3xl\">\n <div className=\"text-center mb-12\">\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\n {t(\"title\", \"Frequently Asked Questions\")}\n </h2>\n <p className=\"text-muted-foreground\">\n {t(\"subtitle\", \"Find answers to common questions about our service.\")}\n </p>\n </div>\n\n <Accordion type=\"single\" collapsible className=\"w-full\">\n {faqItems.map((item, index) => (\n <AccordionItem key={item.id} value={`item-${index}`}>\n <AccordionTrigger className=\"text-left font-semibold hover:no-underline\">\n {item.question}\n </AccordionTrigger>\n <AccordionContent className=\"text-muted-foreground\">\n {item.answer}\n </AccordionContent>\n </AccordionItem>\n ))}\n </Accordion>\n </div>\n </section>\n );\n}\n"
|
|
21
|
+
"content": "import { useTranslation } from \"react-i18next\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport {\r\n Accordion,\r\n AccordionContent,\r\n AccordionItem,\r\n AccordionTrigger,\r\n} from \"@/components/ui/accordion\";\r\n\r\ninterface FaqSimpleProps {\r\n className?: string;\r\n}\r\n\r\nexport function FaqSimple({ className }: FaqSimpleProps) {\r\n const { t } = useTranslation(\"faq-simple\");\r\n\r\n const faqItems = [\r\n {\r\n id: \"faq-1\",\r\n question: t(\"q1\", \"What is included in the free plan?\"),\r\n answer: t(\"a1\", \"The free plan includes basic features such as up to 3 projects, 1GB storage, and email support. Perfect for individuals and small teams getting started.\"),\r\n },\r\n {\r\n id: \"faq-2\",\r\n question: t(\"q2\", \"Can I upgrade or downgrade my plan?\"),\r\n answer: t(\"a2\", \"Yes, you can upgrade or downgrade your plan at any time. Changes will be reflected in your next billing cycle. No penalties for switching plans.\"),\r\n },\r\n {\r\n id: \"faq-3\",\r\n question: t(\"q3\", \"How do I cancel my subscription?\"),\r\n answer: t(\"a3\", \"You can cancel your subscription from your account settings. Your access will continue until the end of your current billing period.\"),\r\n },\r\n {\r\n id: \"faq-4\",\r\n question: t(\"q4\", \"Is my data secure?\"),\r\n answer: t(\"a4\", \"Yes, we take security seriously. All data is encrypted at rest and in transit. We comply with industry standards and regularly undergo security audits.\"),\r\n },\r\n {\r\n id: \"faq-5\",\r\n question: t(\"q5\", \"Do you offer customer support?\"),\r\n answer: t(\"a5\", \"We offer 24/7 customer support via email and live chat. Premium plans also include phone support and dedicated account managers.\"),\r\n },\r\n {\r\n id: \"faq-6\",\r\n question: t(\"q6\", \"Can I get a refund?\"),\r\n answer: t(\"a6\", \"We offer a 30-day money-back guarantee for all paid plans. If you're not satisfied, contact our support team for a full refund.\"),\r\n },\r\n ];\r\n\r\n return (\r\n <section className={cn(\"py-16 md:py-24\", className)}>\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4 max-w-3xl\">\r\n <div className=\"text-center mb-12\">\r\n <h2 className=\"text-3xl font-bold md:text-4xl mb-4\">\r\n {t(\"title\", \"Frequently Asked Questions\")}\r\n </h2>\r\n <p className=\"text-muted-foreground\">\r\n {t(\"subtitle\", \"Find answers to common questions about our service.\")}\r\n </p>\r\n </div>\r\n\r\n <Accordion type=\"single\" collapsible className=\"w-full\">\r\n {faqItems.map((item, index) => (\r\n <AccordionItem key={item.id} value={`item-${index}`}>\r\n <AccordionTrigger className=\"text-left font-semibold hover:no-underline\">\r\n {item.question}\r\n </AccordionTrigger>\r\n <AccordionContent className=\"text-muted-foreground\">\r\n {item.answer}\r\n </AccordionContent>\r\n </AccordionItem>\r\n ))}\r\n </Accordion>\r\n </div>\r\n </section>\r\n );\r\n}\r\n"
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
"path": "faq-simple/lang/en.json",
|
|
25
25
|
"type": "registry:lang",
|
|
26
26
|
"target": "$modules$/faq-simple/lang/en.json",
|
|
27
|
-
"content": "{\n \"title\": \"Frequently Asked Questions\",\n \"subtitle\": \"Find answers to common questions. Contact us if you need more help.\",\n \"q1\": \"How do I get started?\",\n \"a1\": \"Getting started is easy. Simply sign up for an account and follow our onboarding guide.\",\n \"q2\": \"What payment methods do you accept?\",\n \"a2\": \"We accept all major credit cards, PayPal, and bank transfers.\",\n \"q3\": \"Can I cancel my subscription anytime?\",\n \"a3\": \"Yes, you can cancel your subscription at any time from your account settings.\",\n \"q4\": \"Do you offer refunds?\",\n \"a4\": \"We offer a 30-day money-back guarantee for all new purchases.\",\n \"q5\": \"How can I contact support?\",\n \"a5\": \"You can reach our support team via email, live chat, or phone during business hours.\",\n \"q6\": \"Is my data secure?\",\n \"a6\": \"Yes, we use industry-standard encryption and security measures to protect your data.\"\n}"
|
|
27
|
+
"content": "{\r\n \"title\": \"Frequently Asked Questions\",\r\n \"subtitle\": \"Find answers to common questions. Contact us if you need more help.\",\r\n \"q1\": \"How do I get started?\",\r\n \"a1\": \"Getting started is easy. Simply sign up for an account and follow our onboarding guide.\",\r\n \"q2\": \"What payment methods do you accept?\",\r\n \"a2\": \"We accept all major credit cards, PayPal, and bank transfers.\",\r\n \"q3\": \"Can I cancel my subscription anytime?\",\r\n \"a3\": \"Yes, you can cancel your subscription at any time from your account settings.\",\r\n \"q4\": \"Do you offer refunds?\",\r\n \"a4\": \"We offer a 30-day money-back guarantee for all new purchases.\",\r\n \"q5\": \"How can I contact support?\",\r\n \"a5\": \"You can reach our support team via email, live chat, or phone during business hours.\",\r\n \"q6\": \"Is my data secure?\",\r\n \"a6\": \"Yes, we use industry-standard encryption and security measures to protect your data.\"\r\n}"
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
"path": "faq-simple/lang/tr.json",
|
|
31
31
|
"type": "registry:lang",
|
|
32
32
|
"target": "$modules$/faq-simple/lang/tr.json",
|
|
33
|
-
"content": "{\n \"title\": \"Sıkça Sorulan Sorular\",\n \"subtitle\": \"Sık sorulan sorulara cevaplar. Daha fazla yardım için iletişime geçin.\",\n \"q1\": \"Nasıl başlarım?\",\n \"a1\": \"Başlamak kolay. Sadece hesap oluşturun ve başlangıç rehberimizi takip edin.\",\n \"q2\": \"Hangi ödeme yöntemlerini kabul ediyorsunuz?\",\n \"a2\": \"Tüm büyük kredi kartlarını, PayPal ve banka havalelerini kabul ediyoruz.\",\n \"q3\": \"Aboneliğimi istediğim zaman iptal edebilir miyim?\",\n \"a3\": \"Evet, aboneliğinizi istediğiniz zaman hesap ayarlarından iptal edebilirsiniz.\",\n \"q4\": \"Para iadesi yapıyor musunuz?\",\n \"a4\": \"Tüm yeni satın alımlar için 30 günlük para iade garantisi sunuyoruz.\",\n \"q5\": \"Destekle nasıl iletişime geçebilirim?\",\n \"a5\": \"Destek ekibimize e-posta, canlı sohbet veya telefon ile mesai saatlerinde ulaşabilirsiniz.\",\n \"q6\": \"Verilerim güvende mi?\",\n \"a6\": \"Evet, verilerinizi korumak için endüstri standardı şifreleme ve güvenlik önlemleri kullanıyoruz.\"\n}"
|
|
33
|
+
"content": "{\r\n \"title\": \"Sıkça Sorulan Sorular\",\r\n \"subtitle\": \"Sık sorulan sorulara cevaplar. Daha fazla yardım için iletişime geçin.\",\r\n \"q1\": \"Nasıl başlarım?\",\r\n \"a1\": \"Başlamak kolay. Sadece hesap oluşturun ve başlangıç rehberimizi takip edin.\",\r\n \"q2\": \"Hangi ödeme yöntemlerini kabul ediyorsunuz?\",\r\n \"a2\": \"Tüm büyük kredi kartlarını, PayPal ve banka havalelerini kabul ediyoruz.\",\r\n \"q3\": \"Aboneliğimi istediğim zaman iptal edebilir miyim?\",\r\n \"a3\": \"Evet, aboneliğinizi istediğiniz zaman hesap ayarlarından iptal edebilirsiniz.\",\r\n \"q4\": \"Para iadesi yapıyor musunuz?\",\r\n \"a4\": \"Tüm yeni satın alımlar için 30 günlük para iade garantisi sunuyoruz.\",\r\n \"q5\": \"Destekle nasıl iletişime geçebilirim?\",\r\n \"a5\": \"Destek ekibimize e-posta, canlı sohbet veya telefon ile mesai saatlerinde ulaşabilirsiniz.\",\r\n \"q6\": \"Verilerim güvende mi?\",\r\n \"a6\": \"Evet, verilerinizi korumak için endüstri standardı şifreleme ve güvenlik önlemleri kullanıyoruz.\"\r\n}"
|
|
34
34
|
}
|
|
35
35
|
],
|
|
36
36
|
"exports": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"path": "favorites-blog-block/index.ts",
|
|
14
14
|
"type": "registry:index",
|
|
15
15
|
"target": "$modules$/favorites-blog-block/index.ts",
|
|
16
|
-
"content": "export * from './favorites-blog-block';\n"
|
|
16
|
+
"content": "export * from './favorites-blog-block';\r\n"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "favorites-blog-block/favorites-blog-block.tsx",
|
|
@@ -17,25 +17,25 @@
|
|
|
17
17
|
"path": "favorites-blog-page/index.ts",
|
|
18
18
|
"type": "registry:index",
|
|
19
19
|
"target": "$modules$/favorites-blog-page/index.ts",
|
|
20
|
-
"content": "export * from './favorites-blog-page';\nexport { FavoritesBlogPage as default } from './favorites-blog-page';\n"
|
|
20
|
+
"content": "export * from './favorites-blog-page';\r\nexport { FavoritesBlogPage as default } from './favorites-blog-page';\r\n"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"path": "favorites-blog-page/favorites-blog-page.tsx",
|
|
24
24
|
"type": "registry:page",
|
|
25
25
|
"target": "$modules$/favorites-blog-page/favorites-blog-page.tsx",
|
|
26
|
-
"content": "import { Link } from \"react-router\";\nimport { Heart, BookOpen } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Layout } from \"@/components/Layout\";\nimport { PostCard } from \"@/modules/post-card/post-card\";\nimport { useTranslation } from \"react-i18next\";\nimport { useBlog } from \"@/modules/blog-core\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\n\nexport function FavoritesBlogPage() {\n const { t } = useTranslation(\"favorites-blog-page\");\n const { favorites, clearFavorites } = useBlog();\n usePageTitle({ title: t(\"title\", \"My Favorites\") });\n\n // Empty State\n if (favorites.length === 0) {\n return (\n <Layout>\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\n <div className=\"text-center max-w-md mx-auto\">\n <Heart className=\"w-16 h-16 text-muted-foreground mx-auto mb-6\" />\n <h1 className=\"text-3xl font-bold text-foreground mb-4\">\n {t(\"noFavoritesYet\", \"No Favorites Yet\")}\n </h1>\n <p className=\"text-muted-foreground mb-8\">\n {t(\n \"noFavoritesDescription\",\n \"Start browsing our blog and add posts to your favorites by clicking the heart icon.\"\n )}\n </p>\n <Button asChild size=\"lg\">\n <Link to=\"/blog\">\n <BookOpen className=\"w-5 h-5 mr-2\" />\n {t(\"browseBlog\", \"Browse Blog\")}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n </Layout>\n );\n }\n\n // Favorites Grid\n return (\n <Layout>\n <div className=\"min-h-screen py-12\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\n {/* Header */}\n <div className=\"flex justify-between items-center mb-8\">\n <div>\n <h1 className=\"text-3xl font-bold mb-2\">\n {t(\"title\", \"My Favorites\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {t(\n \"favoritesCount\",\n `You have ${favorites.length} favorite posts`\n )}\n </p>\n </div>\n <Button variant=\"outline\" onClick={clearFavorites}>\n {t(\"clearAll\", \"Clear All\")}\n </Button>\n </div>\n\n {/* Posts Grid */}\n <div className=\"grid gap-6 md:grid-cols-2 lg:grid-cols-3\">\n {favorites.map((post) => (\n <PostCard key={post.id} post={post} layout=\"grid\" />\n ))}\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default FavoritesBlogPage;\n"
|
|
26
|
+
"content": "import { Link } from \"react-router\";\r\nimport { Heart, BookOpen } from \"lucide-react\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Layout } from \"@/components/Layout\";\r\nimport { PostCard } from \"@/modules/post-card/post-card\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { useBlog } from \"@/modules/blog-core\";\r\nimport { usePageTitle } from \"@/hooks/use-page-title\";\r\n\r\nexport function FavoritesBlogPage() {\r\n const { t } = useTranslation(\"favorites-blog-page\");\r\n const { favorites, clearFavorites } = useBlog();\r\n usePageTitle({ title: t(\"title\", \"My Favorites\") });\r\n\r\n // Empty State\r\n if (favorites.length === 0) {\r\n return (\r\n <Layout>\r\n <div className=\"min-h-screen bg-muted/30 py-12\">\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n <div className=\"text-center max-w-md mx-auto\">\r\n <Heart className=\"w-16 h-16 text-muted-foreground mx-auto mb-6\" />\r\n <h1 className=\"text-3xl font-bold text-foreground mb-4\">\r\n {t(\"noFavoritesYet\", \"No Favorites Yet\")}\r\n </h1>\r\n <p className=\"text-muted-foreground mb-8\">\r\n {t(\r\n \"noFavoritesDescription\",\r\n \"Start browsing our blog and add posts to your favorites by clicking the heart icon.\"\r\n )}\r\n </p>\r\n <Button asChild size=\"lg\">\r\n <Link to=\"/blog\">\r\n <BookOpen className=\"w-5 h-5 mr-2\" />\r\n {t(\"browseBlog\", \"Browse Blog\")}\r\n </Link>\r\n </Button>\r\n </div>\r\n </div>\r\n </div>\r\n </Layout>\r\n );\r\n }\r\n\r\n // Favorites Grid\r\n return (\r\n <Layout>\r\n <div className=\"min-h-screen py-12\">\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n {/* Header */}\r\n <div className=\"flex justify-between items-center mb-8\">\r\n <div>\r\n <h1 className=\"text-3xl font-bold mb-2\">\r\n {t(\"title\", \"My Favorites\")}\r\n </h1>\r\n <p className=\"text-muted-foreground\">\r\n {t(\r\n \"favoritesCount\",\r\n `You have ${favorites.length} favorite posts`\r\n )}\r\n </p>\r\n </div>\r\n <Button variant=\"outline\" onClick={clearFavorites}>\r\n {t(\"clearAll\", \"Clear All\")}\r\n </Button>\r\n </div>\r\n\r\n {/* Posts Grid */}\r\n <div className=\"grid gap-6 md:grid-cols-2 lg:grid-cols-3\">\r\n {favorites.map((post) => (\r\n <PostCard key={post.id} post={post} layout=\"grid\" />\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n </Layout>\r\n );\r\n}\r\n\r\nexport default FavoritesBlogPage;\r\n"
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
"path": "favorites-blog-page/lang/en.json",
|
|
30
30
|
"type": "registry:lang",
|
|
31
31
|
"target": "$modules$/favorites-blog-page/lang/en.json",
|
|
32
|
-
"content": "{\n \"title\": \"My Favorites\",\n \"noFavoritesYet\": \"No Favorites Yet\",\n \"noFavoritesDescription\": \"Start browsing our blog and add posts to your favorites by clicking the heart icon.\",\n \"browseBlog\": \"Browse Blog\",\n \"favoritesCount\": \"favorite posts\",\n \"clearAll\": \"Clear All\"\n}\n"
|
|
32
|
+
"content": "{\r\n \"title\": \"My Favorites\",\r\n \"noFavoritesYet\": \"No Favorites Yet\",\r\n \"noFavoritesDescription\": \"Start browsing our blog and add posts to your favorites by clicking the heart icon.\",\r\n \"browseBlog\": \"Browse Blog\",\r\n \"favoritesCount\": \"favorite posts\",\r\n \"clearAll\": \"Clear All\"\r\n}\r\n"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"path": "favorites-blog-page/lang/tr.json",
|
|
36
36
|
"type": "registry:lang",
|
|
37
37
|
"target": "$modules$/favorites-blog-page/lang/tr.json",
|
|
38
|
-
"content": "{\n \"title\": \"Favorilerim\",\n \"noFavoritesYet\": \"Henüz Favori Yok\",\n \"noFavoritesDescription\": \"Blog yazılarımıza göz atın ve kalp ikonuna tıklayarak favorilerinize ekleyin.\",\n \"browseBlog\": \"Bloga Göz At\",\n \"favoritesCount\": \"favori yazı\",\n \"clearAll\": \"Tümünü Temizle\"\n}\n"
|
|
38
|
+
"content": "{\r\n \"title\": \"Favorilerim\",\r\n \"noFavoritesYet\": \"Henüz Favori Yok\",\r\n \"noFavoritesDescription\": \"Blog yazılarımıza göz atın ve kalp ikonuna tıklayarak favorilerinize ekleyin.\",\r\n \"browseBlog\": \"Bloga Göz At\",\r\n \"favoritesCount\": \"favori yazı\",\r\n \"clearAll\": \"Tümünü Temizle\"\r\n}\r\n"
|
|
39
39
|
}
|
|
40
40
|
],
|
|
41
41
|
"exports": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"path": "favorites-ecommerce-block/index.ts",
|
|
14
14
|
"type": "registry:index",
|
|
15
15
|
"target": "$modules$/favorites-ecommerce-block/index.ts",
|
|
16
|
-
"content": "export * from './favorites-ecommerce-block';\n"
|
|
16
|
+
"content": "export * from './favorites-ecommerce-block';\r\n"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "favorites-ecommerce-block/favorites-ecommerce-block.tsx",
|
|
@@ -17,25 +17,25 @@
|
|
|
17
17
|
"path": "favorites-ecommerce-page/index.ts",
|
|
18
18
|
"type": "registry:index",
|
|
19
19
|
"target": "$modules$/favorites-ecommerce-page/index.ts",
|
|
20
|
-
"content": "export * from './favorites-ecommerce-page';\nexport { FavoritesEcommercePage as default } from './favorites-ecommerce-page';\n"
|
|
20
|
+
"content": "export * from './favorites-ecommerce-page';\r\nexport { FavoritesEcommercePage as default } from './favorites-ecommerce-page';\r\n"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"path": "favorites-ecommerce-page/favorites-ecommerce-page.tsx",
|
|
24
24
|
"type": "registry:page",
|
|
25
25
|
"target": "$modules$/favorites-ecommerce-page/favorites-ecommerce-page.tsx",
|
|
26
|
-
"content": "import { Link } from \"react-router\";\nimport { Heart, ShoppingBag } from \"lucide-react\";\nimport { Button } from \"@/components/ui/button\";\nimport { Layout } from \"@/components/Layout\";\nimport { ProductCard } from \"@/modules/product-card/product-card\";\nimport { useTranslation } from \"react-i18next\";\nimport { useFavorites } from \"@/modules/ecommerce-core\";\nimport { usePageTitle } from \"@/hooks/use-page-title\";\n\nexport function FavoritesEcommercePage() {\n const { t } = useTranslation(\"favorites-ecommerce-page\");\n const { favorites, clearFavorites } = useFavorites();\n usePageTitle({ title: t(\"title\", \"My Favorites\") });\n\n // Empty State\n if (favorites.length === 0) {\n return (\n <Layout>\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\n <div className=\"text-center max-w-md mx-auto\">\n <Heart className=\"w-16 h-16 text-muted-foreground mx-auto mb-6\" />\n <h1 className=\"text-3xl font-bold text-foreground mb-4\">\n {t(\"noFavoritesYet\", \"No Favorites Yet\")}\n </h1>\n <p className=\"text-muted-foreground mb-8\">\n {t(\n \"noFavoritesDescription\",\n \"Start browsing our products and add items to your favorites by clicking the heart icon.\"\n )}\n </p>\n <Button asChild size=\"lg\">\n <Link to=\"/products\">\n <ShoppingBag className=\"w-5 h-5 mr-2\" />\n {t(\"browseProducts\", \"Browse Products\")}\n </Link>\n </Button>\n </div>\n </div>\n </div>\n </Layout>\n );\n }\n\n // Favorites Grid\n return (\n <Layout>\n <div className=\"min-h-screen bg-muted/30 py-12\">\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-8\">\n <div>\n <h1 className=\"text-3xl font-bold text-foreground mb-2\">\n {t(\"title\", \"My Favorites\")}\n </h1>\n <p className=\"text-muted-foreground\">\n {favorites.length}{\" \"}\n {t(\n \"itemsInFavorites\",\n `item${favorites.length !== 1 ? \"s\" : \"\"} in your favorites`\n )}\n </p>\n </div>\n <Button variant=\"outline\" onClick={clearFavorites}>\n {t(\"clearAll\", \"Clear All\")}\n </Button>\n </div>\n\n {/* Products Grid */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6\">\n {favorites.map((product) => (\n <ProductCard key={product.id} product={product} variant=\"grid\" />\n ))}\n </div>\n </div>\n </div>\n </Layout>\n );\n}\n\nexport default FavoritesEcommercePage;\n"
|
|
26
|
+
"content": "import { Link } from \"react-router\";\r\nimport { Heart, ShoppingBag } from \"lucide-react\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Layout } from \"@/components/Layout\";\r\nimport { ProductCard } from \"@/modules/product-card/product-card\";\r\nimport { useTranslation } from \"react-i18next\";\r\nimport { useFavorites } from \"@/modules/ecommerce-core\";\r\nimport { usePageTitle } from \"@/hooks/use-page-title\";\r\n\r\nexport function FavoritesEcommercePage() {\r\n const { t } = useTranslation(\"favorites-ecommerce-page\");\r\n const { favorites, clearFavorites } = useFavorites();\r\n usePageTitle({ title: t(\"title\", \"My Favorites\") });\r\n\r\n // Empty State\r\n if (favorites.length === 0) {\r\n return (\r\n <Layout>\r\n <div className=\"min-h-screen bg-muted/30 py-12\">\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n <div className=\"text-center max-w-md mx-auto\">\r\n <Heart className=\"w-16 h-16 text-muted-foreground mx-auto mb-6\" />\r\n <h1 className=\"text-3xl font-bold text-foreground mb-4\">\r\n {t(\"noFavoritesYet\", \"No Favorites Yet\")}\r\n </h1>\r\n <p className=\"text-muted-foreground mb-8\">\r\n {t(\r\n \"noFavoritesDescription\",\r\n \"Start browsing our products and add items to your favorites by clicking the heart icon.\"\r\n )}\r\n </p>\r\n <Button asChild size=\"lg\">\r\n <Link to=\"/products\">\r\n <ShoppingBag className=\"w-5 h-5 mr-2\" />\r\n {t(\"browseProducts\", \"Browse Products\")}\r\n </Link>\r\n </Button>\r\n </div>\r\n </div>\r\n </div>\r\n </Layout>\r\n );\r\n }\r\n\r\n // Favorites Grid\r\n return (\r\n <Layout>\r\n <div className=\"min-h-screen bg-muted/30 py-12\">\r\n <div className=\"w-full max-w-[var(--container-max-width)] mx-auto px-4\">\r\n {/* Header */}\r\n <div className=\"flex items-center justify-between mb-8\">\r\n <div>\r\n <h1 className=\"text-3xl font-bold text-foreground mb-2\">\r\n {t(\"title\", \"My Favorites\")}\r\n </h1>\r\n <p className=\"text-muted-foreground\">\r\n {favorites.length}{\" \"}\r\n {t(\r\n \"itemsInFavorites\",\r\n `item${favorites.length !== 1 ? \"s\" : \"\"} in your favorites`\r\n )}\r\n </p>\r\n </div>\r\n <Button variant=\"outline\" onClick={clearFavorites}>\r\n {t(\"clearAll\", \"Clear All\")}\r\n </Button>\r\n </div>\r\n\r\n {/* Products Grid */}\r\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6\">\r\n {favorites.map((product) => (\r\n <ProductCard key={product.id} product={product} variant=\"grid\" />\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n </Layout>\r\n );\r\n}\r\n\r\nexport default FavoritesEcommercePage;\r\n"
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
"path": "favorites-ecommerce-page/lang/en.json",
|
|
30
30
|
"type": "registry:lang",
|
|
31
31
|
"target": "$modules$/favorites-ecommerce-page/lang/en.json",
|
|
32
|
-
"content": "{\n \"title\": \"My Favorites\",\n \"noFavoritesYet\": \"No Favorites Yet\",\n \"noFavoritesDescription\": \"Start browsing our products and add items to your favorites by clicking the heart icon.\",\n \"browseProducts\": \"Browse Products\",\n \"itemsInFavorites\": \"items in your favorites\",\n \"clearAll\": \"Clear All\"\n}\n"
|
|
32
|
+
"content": "{\r\n \"title\": \"My Favorites\",\r\n \"noFavoritesYet\": \"No Favorites Yet\",\r\n \"noFavoritesDescription\": \"Start browsing our products and add items to your favorites by clicking the heart icon.\",\r\n \"browseProducts\": \"Browse Products\",\r\n \"itemsInFavorites\": \"items in your favorites\",\r\n \"clearAll\": \"Clear All\"\r\n}\r\n"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"path": "favorites-ecommerce-page/lang/tr.json",
|
|
36
36
|
"type": "registry:lang",
|
|
37
37
|
"target": "$modules$/favorites-ecommerce-page/lang/tr.json",
|
|
38
|
-
"content": "{\n \"title\": \"Favorilerim\",\n \"noFavoritesYet\": \"Henüz Favori Yok\",\n \"noFavoritesDescription\": \"Ürünlerimize göz atın ve kalp ikonuna tıklayarak favorilerinize ekleyin.\",\n \"browseProducts\": \"Ürünlere Göz At\",\n \"itemsInFavorites\": \"ürün favorilerinizde\",\n \"clearAll\": \"Tümünü Temizle\"\n}\n"
|
|
38
|
+
"content": "{\r\n \"title\": \"Favorilerim\",\r\n \"noFavoritesYet\": \"Henüz Favori Yok\",\r\n \"noFavoritesDescription\": \"Ürünlerimize göz atın ve kalp ikonuna tıklayarak favorilerinize ekleyin.\",\r\n \"browseProducts\": \"Ürünlere Göz At\",\r\n \"itemsInFavorites\": \"ürün favorilerinizde\",\r\n \"clearAll\": \"Tümünü Temizle\"\r\n}\r\n"
|
|
39
39
|
}
|
|
40
40
|
],
|
|
41
41
|
"exports": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"path": "feature-section/index.ts",
|
|
11
11
|
"type": "registry:index",
|
|
12
12
|
"target": "$modules$/feature-section/index.ts",
|
|
13
|
-
"content": "export * from './feature-section';\n"
|
|
13
|
+
"content": "export * from './feature-section';\r\n"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"path": "feature-section/feature-section.tsx",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"path": "feature-section/lang/en.json",
|
|
23
23
|
"type": "registry:lang",
|
|
24
24
|
"target": "$modules$/feature-section/lang/en.json",
|
|
25
|
-
"content": "{\n \"heading\": \"Your Site Title Here\",\n \"description\": \"Discover powerful features designed to help you succeed.\",\n \"feature1\": \"Key feature or benefit #1\",\n \"feature2\": \"Key feature or benefit #2\",\n \"feature3\": \"Key feature or benefit #3\",\n \"primaryButton\": \"Learn More\",\n \"secondaryButton\": \"Get Started\",\n \"imageAlt\": \"Site Preview\"\n}"
|
|
25
|
+
"content": "{\r\n \"heading\": \"Your Site Title Here\",\r\n \"description\": \"Discover powerful features designed to help you succeed.\",\r\n \"feature1\": \"Key feature or benefit #1\",\r\n \"feature2\": \"Key feature or benefit #2\",\r\n \"feature3\": \"Key feature or benefit #3\",\r\n \"primaryButton\": \"Learn More\",\r\n \"secondaryButton\": \"Get Started\",\r\n \"imageAlt\": \"Site Preview\"\r\n}"
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
"path": "feature-section/lang/tr.json",
|
|
29
29
|
"type": "registry:lang",
|
|
30
30
|
"target": "$modules$/feature-section/lang/tr.json",
|
|
31
|
-
"content": "{\n \"heading\": \"Site Başlığınız\",\n \"description\": \"Başarılı olmanıza yardımcı olmak için tasarlanmış güçlü özellikleri keşfedin.\",\n \"feature1\": \"Anahtar özellik veya avantaj #1\",\n \"feature2\": \"Anahtar özellik veya avantaj #2\",\n \"feature3\": \"Anahtar özellik veya avantaj #3\",\n \"primaryButton\": \"Daha Fazla Öğren\",\n \"secondaryButton\": \"Başlayın\",\n \"imageAlt\": \"Site Önizlemesi\"\n}"
|
|
31
|
+
"content": "{\r\n \"heading\": \"Site Başlığınız\",\r\n \"description\": \"Başarılı olmanıza yardımcı olmak için tasarlanmış güçlü özellikleri keşfedin.\",\r\n \"feature1\": \"Anahtar özellik veya avantaj #1\",\r\n \"feature2\": \"Anahtar özellik veya avantaj #2\",\r\n \"feature3\": \"Anahtar özellik veya avantaj #3\",\r\n \"primaryButton\": \"Daha Fazla Öğren\",\r\n \"secondaryButton\": \"Başlayın\",\r\n \"imageAlt\": \"Site Önizlemesi\"\r\n}"
|
|
32
32
|
}
|
|
33
33
|
],
|
|
34
34
|
"exports": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"path": "featured-products/index.ts",
|
|
14
14
|
"type": "registry:index",
|
|
15
15
|
"target": "$modules$/featured-products/index.ts",
|
|
16
|
-
"content": "export * from './featured-products';\n"
|
|
16
|
+
"content": "export * from './featured-products';\r\n"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "featured-products/featured-products.tsx",
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
"path": "featured-products/lang/en.json",
|
|
26
26
|
"type": "registry:lang",
|
|
27
27
|
"target": "$modules$/featured-products/lang/en.json",
|
|
28
|
-
"content": "{\n \"title\": \"Featured Products\",\n \"subtitle\": \"Hand-picked favorites from our collection\",\n \"viewAll\": \"View All Products\"\n}\n"
|
|
28
|
+
"content": "{\r\n \"title\": \"Featured Products\",\r\n \"subtitle\": \"Hand-picked favorites from our collection\",\r\n \"viewAll\": \"View All Products\"\r\n}\r\n"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"path": "featured-products/lang/tr.json",
|
|
32
32
|
"type": "registry:lang",
|
|
33
33
|
"target": "$modules$/featured-products/lang/tr.json",
|
|
34
|
-
"content": "{\n \"title\": \"Öne Çıkan Ürünler\",\n \"subtitle\": \"Koleksiyonumuzdan özenle seçilmiş favoriler\",\n \"viewAll\": \"Tüm Ürünleri Gör\"\n}\n"
|
|
34
|
+
"content": "{\r\n \"title\": \"Öne Çıkan Ürünler\",\r\n \"subtitle\": \"Koleksiyonumuzdan özenle seçilmiş favoriler\",\r\n \"viewAll\": \"Tüm Ürünleri Gör\"\r\n}\r\n"
|
|
35
35
|
}
|
|
36
36
|
],
|
|
37
37
|
"exports": {
|