diff --git a/src/app/admin/subscriptions/createSubscription/page.tsx b/src/app/admin/subscriptions/createSubscription/page.tsx index 89068c5..0c93d48 100644 --- a/src/app/admin/subscriptions/createSubscription/page.tsx +++ b/src/app/admin/subscriptions/createSubscription/page.tsx @@ -15,35 +15,25 @@ export default function CreateSubscriptionPage() { // form state const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); - const [quantity, setQuantity] = useState(1); const [price, setPrice] = useState(0); const [state, setState] = useState<'available'|'unavailable'>('available'); const [pictureFile, setPictureFile] = useState(undefined); const [currency, setCurrency] = useState('EUR'); - const [taxRate, setTaxRate] = useState(undefined); const [isFeatured, setIsFeatured] = useState(false); - const [billingInterval, setBillingInterval] = useState<'day'|'week'|'month'|'year'|''>(''); - const [intervalCount, setIntervalCount] = useState(undefined); - const [sku, setSku] = useState(''); - const [slug, setSlug] = useState(''); + // Fixed billing defaults (locked: month / 1) + const billingInterval: 'month' = 'month'; + const intervalCount: number = 1; const onCreate = async (e: React.FormEvent) => { e.preventDefault(); setError(null); try { - const normalizedIntervalCount = billingInterval ? (intervalCount && intervalCount > 0 ? intervalCount : 1) : undefined; await createProduct({ title, description, - quantity, price, currency, - tax_rate: taxRate, is_featured: isFeatured, - billing_interval: billingInterval || undefined, - interval_count: normalizedIntervalCount, - sku: sku || undefined, - slug: slug || undefined, state: state === 'available', pictureFile }); @@ -81,11 +71,6 @@ export default function CreateSubscriptionPage() { setTitle(e.target.value)} /> - {/* Quantity */} -
- - setQuantity(Number(e.target.value))} /> -
{/* Price */}
@@ -96,49 +81,19 @@ export default function CreateSubscriptionPage() { setCurrency(e.target.value.toUpperCase().slice(0,3))} />
- {/* Tax Rate */} -
- - setTaxRate(e.target.value === '' ? undefined : Number(e.target.value))} /> -
{/* Featured */}
setIsFeatured(e.target.checked)} />
- {/* Billing Interval */} -
- - -
- {/* Interval Count */} -
- - setIntervalCount(e.target.value === '' ? undefined : Number(e.target.value))} disabled={!billingInterval} /> -
- {/* SKU */} -
- - setSku(e.target.value)} /> -
- {/* Slug */} -
- - setSlug(e.target.value)} /> + {/* Fixed Billing (Locked) */} +
+ +

Fixed monthly billing (interval count = 1). These settings are locked.

+
+ + +
{/* Availability */}
diff --git a/src/app/admin/subscriptions/hooks/useCoffeeManagement.ts b/src/app/admin/subscriptions/hooks/useCoffeeManagement.ts index 7949778..6cde324 100644 --- a/src/app/admin/subscriptions/hooks/useCoffeeManagement.ts +++ b/src/app/admin/subscriptions/hooks/useCoffeeManagement.ts @@ -5,15 +5,11 @@ export type CoffeeItem = { id: number; title: string; description: string; - quantity: number; price: number; currency?: string; - tax_rate?: number; is_featured?: boolean; billing_interval?: 'day'|'week'|'month'|'year'|null; interval_count?: number|null; - sku?: string|null; - slug?: string|null; object_storage_id?: string|null; original_filename?: string|null; state: boolean; @@ -72,13 +68,10 @@ export default function useCoffeeManagement() { const listProducts = useCallback(async (): Promise => { const data = await authorizedFetch('/api/admin/coffee', { method: 'GET' }); if (!Array.isArray(data)) return []; - // Normalize numeric fields in case API returns strings (e.g., MySQL DECIMAL) return data.map((r: any) => ({ ...r, id: Number(r.id), - quantity: r.quantity != null ? Number(r.quantity) : 0, price: r.price != null && r.price !== '' ? Number(r.price) : 0, - tax_rate: r.tax_rate != null && r.tax_rate !== '' ? Number(r.tax_rate) : undefined, interval_count: r.interval_count != null && r.interval_count !== '' ? Number(r.interval_count) : null, state: !!r.state, })) as CoffeeItem[]; @@ -87,31 +80,22 @@ export default function useCoffeeManagement() { const createProduct = useCallback(async (payload: { title: string; description: string; - quantity: number; price: number; currency?: string; - tax_rate?: number; is_featured?: boolean; - billing_interval?: 'day'|'week'|'month'|'year'; - interval_count?: number; - sku?: string; - slug?: string; state?: boolean; pictureFile?: File; }): Promise => { const fd = new FormData(); fd.append('title', payload.title); fd.append('description', payload.description); - fd.append('quantity', String(payload.quantity)); fd.append('price', String(payload.price)); if (payload.currency) fd.append('currency', payload.currency); - if (typeof payload.tax_rate === 'number') fd.append('tax_rate', String(payload.tax_rate)); if (typeof payload.is_featured === 'boolean') fd.append('is_featured', String(payload.is_featured)); - if (payload.billing_interval) fd.append('billing_interval', payload.billing_interval); - if (typeof payload.interval_count === 'number') fd.append('interval_count', String(payload.interval_count)); - if (payload.sku) fd.append('sku', payload.sku); - if (payload.slug) fd.append('slug', payload.slug); if (typeof payload.state === 'boolean') fd.append('state', String(payload.state)); + // Fixed billing defaults + fd.append('billing_interval', 'month'); + fd.append('interval_count', '1'); if (payload.pictureFile) fd.append('picture', payload.pictureFile); return authorizedFetch('/api/admin/coffee', { method: 'POST', body: fd }); }, [authorizedFetch]); @@ -119,31 +103,22 @@ export default function useCoffeeManagement() { const updateProduct = useCallback(async (id: number, payload: Partial<{ title: string; description: string; - quantity: number; price: number; currency: string; - tax_rate: number; is_featured: boolean; - billing_interval: 'day'|'week'|'month'|'year'; - interval_count: number; - sku: string; - slug: string; state: boolean; pictureFile: File; }>): Promise => { const fd = new FormData(); if (payload.title !== undefined) fd.append('title', String(payload.title)); if (payload.description !== undefined) fd.append('description', String(payload.description)); - if (payload.quantity !== undefined) fd.append('quantity', String(payload.quantity)); if (payload.price !== undefined) fd.append('price', String(payload.price)); if (payload.currency !== undefined) fd.append('currency', payload.currency); - if (payload.tax_rate !== undefined) fd.append('tax_rate', String(payload.tax_rate)); if (payload.is_featured !== undefined) fd.append('is_featured', String(payload.is_featured)); - if (payload.billing_interval !== undefined) fd.append('billing_interval', payload.billing_interval); - if (payload.interval_count !== undefined) fd.append('interval_count', String(payload.interval_count)); - if (payload.sku !== undefined) fd.append('sku', payload.sku); - if (payload.slug !== undefined) fd.append('slug', payload.slug); if (payload.state !== undefined) fd.append('state', String(payload.state)); + // Keep fixed defaults + fd.append('billing_interval', 'month'); + fd.append('interval_count', '1'); if (payload.pictureFile) fd.append('picture', payload.pictureFile); return authorizedFetch(`/api/admin/coffee/${id}`, { method: 'PUT', body: fd }); }, [authorizedFetch]); diff --git a/src/app/admin/subscriptions/page.tsx b/src/app/admin/subscriptions/page.tsx index de6f3b4..06115af 100644 --- a/src/app/admin/subscriptions/page.tsx +++ b/src/app/admin/subscriptions/page.tsx @@ -35,20 +35,22 @@ export default function AdminSubscriptionsPage() { ); + const [deleteTarget, setDeleteTarget] = useState(null); + return (
{/* Header */} -
-
+
+

Subscription Products

Manage all products and subscription plans.

Create Subscription @@ -74,22 +76,16 @@ export default function AdminSubscriptionsPage() { {item.title} )}

{item.description}

-
-
-
Qty
-
{item.quantity}
-
+
Price
- {item.currency || 'EUR'}{' '} - {Number.isFinite(Number(item.price)) ? Number(item.price).toFixed(2) : String(item.price)} - {Number.isFinite(Number(item.tax_rate)) ? ` + ${Number(item.tax_rate)}%` : ''} + {item.currency || 'EUR'} {Number.isFinite(Number(item.price)) ? Number(item.price).toFixed(2) : String(item.price)}
{item.billing_interval && item.interval_count ? ( -
- Every {item.interval_count} {item.billing_interval}{item.interval_count > 1 ? 's' : ''} +
+ Billing: {item.billing_interval} (x{item.interval_count})
) : null}
@@ -105,7 +101,7 @@ export default function AdminSubscriptionsPage() { @@ -116,6 +112,34 @@ export default function AdminSubscriptionsPage() {
No subscriptions found.
)}
+ {/* Confirm Delete Modal */} + {deleteTarget && ( +
+
setDeleteTarget(null)} /> +
+
+
+

Delete subscription?

+

You are about to delete "{deleteTarget.title}". This action cannot be undone.

+
+
+ + +
+
+
+
+ )}
diff --git a/src/app/components/nav/Header.tsx b/src/app/components/nav/Header.tsx index 7c1f872..734b457 100644 --- a/src/app/components/nav/Header.tsx +++ b/src/app/components/nav/Header.tsx @@ -42,6 +42,9 @@ const navLinks = [ { name: 'About us', href: '/about-us' }, ]; +// Toggle visibility of Shop navigation across header (desktop + mobile) +const showShop = false; + export default function Header() { const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const [isDark, setIsDark] = useState(false) @@ -244,46 +247,47 @@ export default function Header() {
- {/* Shop dropdown stays first */} - - - Shop - - {/* ...existing Shop PopoverPanel... */} - -
-
- {shopItems.map(item => ( -
-
-
- -

{item.description}

-
- ))} +
+
+ +

{item.description}

+
+ ))} +
-
- - + + + )} {/* Affiliate Links */} - - {/* Category Badge */}
- - {product.category} - + {product.category}
- - {/* Out of Stock Overlay */} {!product.inStock && (
- - Ausverkauft - + Ausverkauft
)}
- - {/* Product Info */}
-

- {product.name} -

- - {/* Rating */} +

{product.name}

{[0, 1, 2, 3, 4].map((rating) => ( -

({product.reviewCount})

@@ -473,21 +206,10 @@ export default function ShopPage() {

{product.price}

- - {/* Add to Cart Button */}