fix(admin): attach jwt auth headers to all protected admin api endpoints
This commit is contained in:
1 parent
911614f862
commit
af703fe2ac
29 files changed
+125
-50
No files matched your search
@@ -15,6 +15,7 @@ import {
|
|||||||
Package
|
Package
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { API_BASE, type Category } from '@homaroma/types';
|
import { API_BASE, type Category } from '@homaroma/types';
|
||||||
|
import { getAuthHeaders } from '../utils/auth';
|
||||||
|
|
||||||
interface CategoryBulkJsonModalProps {
|
interface CategoryBulkJsonModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -256,7 +257,7 @@ ${JSON.stringify(
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/categories/bulk-import`, {
|
const res = await fetch(`${API_BASE}/categories/bulk-import`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify(parsedImportData),
|
body: JSON.stringify(parsedImportData),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
Filter
|
Filter
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { API_BASE, type AttributeDefinition, type Category } from '@homaroma/types';
|
import { API_BASE, type AttributeDefinition, type Category } from '@homaroma/types';
|
||||||
|
import { getAuthHeaders } from '../utils/auth';
|
||||||
|
|
||||||
interface ProductAttributeBulkJsonModalProps {
|
interface ProductAttributeBulkJsonModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -370,7 +371,7 @@ Please output ONLY the valid JSON Array without surrounding markdown or explanat
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/product-attributes/definitions/bulk-import`, {
|
const res = await fetch(`${API_BASE}/product-attributes/definitions/bulk-import`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify(parsedImportData),
|
body: JSON.stringify(parsedImportData),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
Sparkles,
|
Sparkles,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { API_BASE, type RawMaterial } from '@homaroma/types';
|
import { API_BASE, type RawMaterial } from '@homaroma/types';
|
||||||
|
import { getAuthHeaders } from '../utils/auth';
|
||||||
|
|
||||||
interface RawMaterialBulkJsonModalProps {
|
interface RawMaterialBulkJsonModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -143,7 +144,7 @@ export default function RawMaterialBulkJsonModal({
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/raw-materials/bulk`, {
|
const res = await fetch(`${API_BASE}/raw-materials/bulk`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify(parsedPreview.items),
|
body: JSON.stringify(parsedPreview.items),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect, useMemo, useRef } from 'react'
|
|||||||
import { useParams, useNavigate, Link } from 'react-router-dom'
|
import { useParams, useNavigate, Link } from 'react-router-dom'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { API_BASE } from '@homaroma/types'
|
import { API_BASE } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import { ArrowLeft, Save, Loader2, Image as ImageIcon } from 'lucide-react'
|
import { ArrowLeft, Save, Loader2, Image as ImageIcon } from 'lucide-react'
|
||||||
import ReactQuill from 'react-quill-new'
|
import ReactQuill from 'react-quill-new'
|
||||||
import 'react-quill-new/dist/quill.snow.css'
|
import 'react-quill-new/dist/quill.snow.css'
|
||||||
@@ -59,7 +60,7 @@ export default function ArticleEditPage() {
|
|||||||
const payload = { title, slug, coverImage, content, isPublished }
|
const payload = { title, slug, coverImage, content, isPublished }
|
||||||
const res = await fetch(`${API_BASE}/articles${isNew ? '' : `/${id}`}`, {
|
const res = await fetch(`${API_BASE}/articles${isNew ? '' : `/${id}`}`, {
|
||||||
method: isNew ? 'POST' : 'PUT',
|
method: isNew ? 'POST' : 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify(payload)
|
body: JSON.stringify(payload)
|
||||||
})
|
})
|
||||||
if (!res.ok) throw new Error(await res.text())
|
if (!res.ok) throw new Error(await res.text())
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { API_BASE, type Article } from '@homaroma/types'
|
import { API_BASE, type Article } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import { Plus, Edit, Trash2, Eye, EyeOff } from 'lucide-react'
|
import { Plus, Edit, Trash2, Eye, EyeOff } from 'lucide-react'
|
||||||
|
|
||||||
export default function ArticlesPage() {
|
export default function ArticlesPage() {
|
||||||
@@ -26,7 +27,10 @@ export default function ArticlesPage() {
|
|||||||
const handleDelete = async (id: number) => {
|
const handleDelete = async (id: number) => {
|
||||||
if (!confirm('Are you sure you want to delete this article?')) return
|
if (!confirm('Are you sure you want to delete this article?')) return
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/articles/${id}`, { method: 'DELETE' })
|
const res = await fetch(`${API_BASE}/articles/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
})
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
setArticles(prev => prev.filter(a => a.id !== id))
|
setArticles(prev => prev.filter(a => a.id !== id))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect, useMemo } from 'react'
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { API_BASE, type Brand } from '@homaroma/types'
|
import { API_BASE, type Brand } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import {
|
import {
|
||||||
Award,
|
Award,
|
||||||
Plus,
|
Plus,
|
||||||
@@ -105,7 +106,7 @@ export default function BrandsPage() {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/products/brands`, {
|
const res = await fetch(`${API_BASE}/products/brands`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({ name: newBrandName.trim() })
|
body: JSON.stringify({ name: newBrandName.trim() })
|
||||||
})
|
})
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
@@ -133,7 +134,7 @@ export default function BrandsPage() {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/products/brands/${editingBrand.id}`, {
|
const res = await fetch(`${API_BASE}/products/brands/${editingBrand.id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({ name: editBrandName.trim() })
|
body: JSON.stringify({ name: editBrandName.trim() })
|
||||||
})
|
})
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
@@ -163,7 +164,8 @@ export default function BrandsPage() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/products/brands/${brand.id}`, {
|
const res = await fetch(`${API_BASE}/products/brands/${brand.id}`, {
|
||||||
method: 'DELETE'
|
method: 'DELETE',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
})
|
})
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
if (res.ok && data.success) {
|
if (res.ok && data.success) {
|
||||||
@@ -200,7 +202,7 @@ export default function BrandsPage() {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/products/brands/${translatingBrand.id}/translations`, {
|
const res = await fetch(`${API_BASE}/products/brands/${translatingBrand.id}/translations`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({ name: translationsMap })
|
body: JSON.stringify({ name: translationsMap })
|
||||||
})
|
})
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect, useMemo } from 'react'
|
|||||||
import { useNavigate, Link } from 'react-router-dom'
|
import { useNavigate, Link } from 'react-router-dom'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { API_BASE, getCategoryImage, resolveCategoryMediaUrl, type Category } from '@homaroma/types'
|
import { API_BASE, getCategoryImage, resolveCategoryMediaUrl, type Category } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import {
|
import {
|
||||||
Edit3,
|
Edit3,
|
||||||
Package,
|
Package,
|
||||||
@@ -75,7 +76,8 @@ export default function CategoriesPage() {
|
|||||||
if (!confirm(t('categories.confirmDelete', `Are you sure you want to delete category "${catName}"?`))) return
|
if (!confirm(t('categories.confirmDelete', `Are you sure you want to delete category "${catName}"?`))) return
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/categories/${id}`, {
|
const res = await fetch(`${API_BASE}/categories/${id}`, {
|
||||||
method: 'DELETE'
|
method: 'DELETE',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
})
|
})
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
fetchData()
|
fetchData()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'
|
|||||||
import { useParams, useNavigate, Link } from 'react-router-dom'
|
import { useParams, useNavigate, Link } from 'react-router-dom'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { API_BASE, type Category, type CategoryMedia, type CategoryMediaRole, type CategoryFeaturedSeries, type CategoryUspBenefit, type CategoryFilterConfig } from '@homaroma/types'
|
import { API_BASE, type Category, type CategoryMedia, type CategoryMediaRole, type CategoryFeaturedSeries, type CategoryUspBenefit, type CategoryFilterConfig } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import {
|
import {
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
Save,
|
Save,
|
||||||
@@ -335,7 +336,7 @@ export default function CategoryEditPage() {
|
|||||||
// Step 1: Save base category
|
// Step 1: Save base category
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method,
|
method,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
name: masterName,
|
name: masterName,
|
||||||
imageUrl: thumbnail.url,
|
imageUrl: thumbnail.url,
|
||||||
@@ -367,7 +368,7 @@ export default function CategoryEditPage() {
|
|||||||
if (targetId) {
|
if (targetId) {
|
||||||
await fetch(`${API_BASE}/categories/${targetId}/translations`, {
|
await fetch(`${API_BASE}/categories/${targetId}/translations`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({ name })
|
body: JSON.stringify({ name })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -396,7 +397,8 @@ export default function CategoryEditPage() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/categories/${id}`, {
|
const res = await fetch(`${API_BASE}/categories/${id}`, {
|
||||||
method: 'DELETE'
|
method: 'DELETE',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
})
|
})
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
navigate('/categories')
|
navigate('/categories')
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { API_BASE } from '@homaroma/types'
|
import { API_BASE } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import { Edit3, Ticket, Trash2, Plus, X, Power, PowerOff } from 'lucide-react'
|
import { Edit3, Ticket, Trash2, Plus, X, Power, PowerOff } from 'lucide-react'
|
||||||
|
|
||||||
interface Coupon {
|
interface Coupon {
|
||||||
@@ -50,7 +51,7 @@ export default function CouponsPage() {
|
|||||||
|
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method,
|
method,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
code: editingCoupon.code,
|
code: editingCoupon.code,
|
||||||
type: editingCoupon.type,
|
type: editingCoupon.type,
|
||||||
@@ -80,7 +81,8 @@ export default function CouponsPage() {
|
|||||||
if (!confirm('Are you sure you want to delete this coupon?')) return
|
if (!confirm('Are you sure you want to delete this coupon?')) return
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/coupons/${id}`, {
|
const res = await fetch(`${API_BASE}/coupons/${id}`, {
|
||||||
method: 'DELETE'
|
method: 'DELETE',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
})
|
})
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
fetchData()
|
fetchData()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { API_BASE } from '@homaroma/types'
|
import { API_BASE } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
|
|
||||||
interface Customer {
|
interface Customer {
|
||||||
id: number
|
id: number
|
||||||
@@ -25,7 +26,9 @@ export default function CustomersPage() {
|
|||||||
|
|
||||||
const fetchCustomers = async () => {
|
const fetchCustomers = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/customers`)
|
const res = await fetch(`${API_BASE}/customers`, {
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
})
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
setCustomers(data)
|
setCustomers(data)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Package, Tag, Layers, TrendingUp } from 'lucide-react'
|
import { Package, Tag, Layers, TrendingUp } from 'lucide-react'
|
||||||
import { API_BASE, type Category, type Product } from '@homaroma/types'
|
import { API_BASE, type Category, type Product } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import { useStoreSettings } from '../contexts/StoreSettingsContext'
|
import { useStoreSettings } from '../contexts/StoreSettingsContext'
|
||||||
|
|
||||||
export default function DashboardPage() {
|
export default function DashboardPage() {
|
||||||
@@ -22,10 +23,10 @@ export default function DashboardPage() {
|
|||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
const [catRes, prodRes, orderRes, customerRes] = await Promise.all([
|
const [catRes, prodRes, orderRes, customerRes] = await Promise.all([
|
||||||
fetch(`${API_BASE}/categories`),
|
fetch(`${API_BASE}/categories`, { headers: getAuthHeaders() }),
|
||||||
fetch(`${API_BASE}/products`),
|
fetch(`${API_BASE}/products`, { headers: getAuthHeaders() }),
|
||||||
fetch(`${API_BASE}/orders`),
|
fetch(`${API_BASE}/orders`, { headers: getAuthHeaders() }),
|
||||||
fetch(`${API_BASE}/customers`)
|
fetch(`${API_BASE}/customers`, { headers: getAuthHeaders() })
|
||||||
])
|
])
|
||||||
const cats: Category[] = await catRes.json()
|
const cats: Category[] = await catRes.json()
|
||||||
const prods: Product[] = await prodRes.json()
|
const prods: Product[] = await prodRes.json()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { API_BASE } from '@homaroma/types';
|
import { API_BASE } from '@homaroma/types';
|
||||||
|
import { getAuthHeaders } from '../utils/auth';
|
||||||
import { Upload, Loader2, Image as ImageIcon, Trash2, ClipboardPaste } from 'lucide-react';
|
import { Upload, Loader2, Image as ImageIcon, Trash2, ClipboardPaste } from 'lucide-react';
|
||||||
|
|
||||||
interface MediaItem {
|
interface MediaItem {
|
||||||
@@ -21,7 +22,9 @@ export default function MediaLibraryPage() {
|
|||||||
const fetchMedia = async () => {
|
const fetchMedia = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const res = await fetch(`${API_BASE}/media`);
|
const res = await fetch(`${API_BASE}/media`, {
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
setMedia(data);
|
setMedia(data);
|
||||||
@@ -113,6 +116,7 @@ export default function MediaLibraryPage() {
|
|||||||
setDeletingId(id);
|
setDeletingId(id);
|
||||||
const res = await fetch(`${API_BASE}/media/${id}`, {
|
const res = await fetch(`${API_BASE}/media/${id}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
await fetchMedia();
|
await fetchMedia();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { API_BASE } from '@homaroma/types'
|
import { API_BASE } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import { Download, Loader2, Mail } from 'lucide-react'
|
import { Download, Loader2, Mail } from 'lucide-react'
|
||||||
|
|
||||||
interface Subscriber {
|
interface Subscriber {
|
||||||
@@ -14,7 +15,9 @@ export default function NewsletterSubscribersPage() {
|
|||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch(`${API_BASE}/newsletter/subscribers`)
|
fetch(`${API_BASE}/newsletter/subscribers`, {
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
})
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'
|
|||||||
import { useParams, Link } from 'react-router-dom'
|
import { useParams, Link } from 'react-router-dom'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { API_BASE, formatPrice, type Order } from '@homaroma/types'
|
import { API_BASE, formatPrice, type Order } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import { ArrowLeft, CheckCircle2, ExternalLink, Loader2, Truck, XCircle, FileImage } from 'lucide-react'
|
import { ArrowLeft, CheckCircle2, ExternalLink, Loader2, Truck, XCircle, FileImage } from 'lucide-react'
|
||||||
|
|
||||||
export default function OrderDetailPage() {
|
export default function OrderDetailPage() {
|
||||||
@@ -13,7 +14,9 @@ export default function OrderDetailPage() {
|
|||||||
|
|
||||||
const fetchOrder = async () => {
|
const fetchOrder = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/orders/${id}`)
|
const res = await fetch(`${API_BASE}/orders/${id}`, {
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
})
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
setOrder(data)
|
setOrder(data)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -32,7 +35,7 @@ export default function OrderDetailPage() {
|
|||||||
try {
|
try {
|
||||||
await fetch(`${API_BASE}/orders/${id}/status`, {
|
await fetch(`${API_BASE}/orders/${id}/status`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({ status })
|
body: JSON.stringify({ status })
|
||||||
})
|
})
|
||||||
await fetchOrder()
|
await fetchOrder()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'
|
|||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { API_BASE, formatPrice, type Order } from '@homaroma/types'
|
import { API_BASE, formatPrice, type Order } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import { Search, ChevronRight, Clock, CheckCircle2, Truck, XCircle } from 'lucide-react'
|
import { Search, ChevronRight, Clock, CheckCircle2, Truck, XCircle } from 'lucide-react'
|
||||||
|
|
||||||
export default function OrdersPage() {
|
export default function OrdersPage() {
|
||||||
@@ -14,7 +15,9 @@ export default function OrdersPage() {
|
|||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/orders`)
|
const res = await fetch(`${API_BASE}/orders`, {
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
})
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
setOrders(Array.isArray(data) ? data : [])
|
setOrders(Array.isArray(data) ? data : [])
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { API_BASE, type StoreSettings } from '@homaroma/types'
|
import { API_BASE, type StoreSettings } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import { Save, Loader2, Landmark, Truck } from 'lucide-react'
|
import { Save, Loader2, Landmark, Truck } from 'lucide-react'
|
||||||
|
|
||||||
export default function PaymentSettingsPage() {
|
export default function PaymentSettingsPage() {
|
||||||
@@ -32,7 +33,7 @@ export default function PaymentSettingsPage() {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/settings`, {
|
const res = await fetch(`${API_BASE}/settings`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
shippingFee: parseFloat(shippingFee) || 0,
|
shippingFee: parseFloat(shippingFee) || 0,
|
||||||
bankName, bankAccountName, bankAccountNumber,
|
bankName, bankAccountName, bankAccountNumber,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect, useMemo } from 'react'
|
|||||||
import { useParams, useNavigate, Link } from 'react-router-dom'
|
import { useParams, useNavigate, Link } from 'react-router-dom'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { API_BASE, type AttributeDefinition, type Category } from '@homaroma/types'
|
import { API_BASE, type AttributeDefinition, type Category } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import {
|
import {
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
Save,
|
Save,
|
||||||
@@ -488,7 +489,7 @@ export default function ProductAttributeEditPage() {
|
|||||||
|
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method,
|
method,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -517,7 +518,10 @@ export default function ProductAttributeEditPage() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
const res = await fetch(`${API_BASE}/product-attributes/definitions/${id}`, { method: 'DELETE' })
|
const res = await fetch(`${API_BASE}/product-attributes/definitions/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
})
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const errData = await res.json().catch(() => null)
|
const errData = await res.json().catch(() => null)
|
||||||
throw new Error(errData?.message || 'Failed to delete attribute definition')
|
throw new Error(errData?.message || 'Failed to delete attribute definition')
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from 'react'
|
|||||||
import { useNavigate, Link } from 'react-router-dom'
|
import { useNavigate, Link } from 'react-router-dom'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { API_BASE, type AttributeDefinition, type Category } from '@homaroma/types'
|
import { API_BASE, type AttributeDefinition, type Category } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import {
|
import {
|
||||||
Edit3,
|
Edit3,
|
||||||
FileJson,
|
FileJson,
|
||||||
@@ -53,7 +54,10 @@ export default function ProductAttributesPage() {
|
|||||||
const handleDelete = async (id: number, label: string) => {
|
const handleDelete = async (id: number, label: string) => {
|
||||||
if (!confirm(t('common.confirmDelete', `Are you sure you want to delete attribute "${label}"?`))) return
|
if (!confirm(t('common.confirmDelete', `Are you sure you want to delete attribute "${label}"?`))) return
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/product-attributes/definitions/${id}`, { method: 'DELETE' })
|
const res = await fetch(`${API_BASE}/product-attributes/definitions/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
})
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const errData = await res.json().catch(() => null)
|
const errData = await res.json().catch(() => null)
|
||||||
throw new Error(errData?.message || 'Failed to delete attribute definition')
|
throw new Error(errData?.message || 'Failed to delete attribute definition')
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
FileJson
|
FileJson
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { API_BASE, type Product } from '@homaroma/types'
|
import { API_BASE, type Product } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import ProductBomPromptModal from '../components/ProductBomPromptModal'
|
import ProductBomPromptModal from '../components/ProductBomPromptModal'
|
||||||
import ProductBomImportModal from '../components/ProductBomImportModal'
|
import ProductBomImportModal from '../components/ProductBomImportModal'
|
||||||
|
|
||||||
@@ -54,7 +55,9 @@ export default function ProductBomPage() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
const rmRes = await fetch(`${API_BASE}/raw-materials`)
|
const rmRes = await fetch(`${API_BASE}/raw-materials`, {
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
})
|
||||||
if (rmRes.ok) {
|
if (rmRes.ok) {
|
||||||
const rmData = await rmRes.json()
|
const rmData = await rmRes.json()
|
||||||
setRawMaterials(rmData)
|
setRawMaterials(rmData)
|
||||||
@@ -126,7 +129,7 @@ export default function ProductBomPage() {
|
|||||||
|
|
||||||
const response = await fetch(`${API_BASE}/products/${id}`, {
|
const response = await fetch(`${API_BASE}/products/${id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
Upload
|
Upload
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { API_BASE, type AttributeDefinition, type Brand, type Category, type Product, type ProductAttribute, type ProductImage, type Variant } from '@homaroma/types'
|
import { API_BASE, type AttributeDefinition, type Brand, type Category, type Product, type ProductAttribute, type ProductImage, type Variant } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import MDEditor from '@uiw/react-md-editor'
|
import MDEditor from '@uiw/react-md-editor'
|
||||||
import MediaLibraryModal from '../components/MediaLibraryModal'
|
import MediaLibraryModal from '../components/MediaLibraryModal'
|
||||||
import ContentLanguageTabs from '../components/ContentLanguageTabs'
|
import ContentLanguageTabs from '../components/ContentLanguageTabs'
|
||||||
@@ -408,7 +409,7 @@ export default function ProductEditPage() {
|
|||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method,
|
method,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
categoryId,
|
categoryId,
|
||||||
brandId: brandId ? Number(brandId) : undefined,
|
brandId: brandId ? Number(brandId) : undefined,
|
||||||
@@ -444,7 +445,7 @@ export default function ProductEditPage() {
|
|||||||
|
|
||||||
const attributeResponse = await fetch(`${API_BASE}/products/${savedProductId}/attributes`, {
|
const attributeResponse = await fetch(`${API_BASE}/products/${savedProductId}/attributes`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
attributes: attributes.map(buildAttributePayload),
|
attributes: attributes.map(buildAttributePayload),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
import { useNavigate, useSearchParams } from 'react-router-dom'
|
import { useNavigate, useSearchParams } from 'react-router-dom'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { API_BASE, getCategoryImage, formatPrice, type Category, type Product } from '@homaroma/types'
|
import { API_BASE, getCategoryImage, formatPrice, type Category, type Product } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import ProductJsonGuideModal from '../components/ProductJsonGuideModal'
|
import ProductJsonGuideModal from '../components/ProductJsonGuideModal'
|
||||||
import ProductOptionUxModal from '../components/products/ProductOptionUxModal'
|
import ProductOptionUxModal from '../components/products/ProductOptionUxModal'
|
||||||
|
|
||||||
@@ -175,7 +176,7 @@ export default function ProductsPage() {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/products/bulk`, {
|
const res = await fetch(`${API_BASE}/products/bulk`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify(payloadArray)
|
body: JSON.stringify(payloadArray)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -237,7 +238,7 @@ export default function ProductsPage() {
|
|||||||
const updatedImages = [...(selectedProduct.images || []), ...newImages]
|
const updatedImages = [...(selectedProduct.images || []), ...newImages]
|
||||||
const updateRes = await fetch(`${API_BASE}/products/${selectedProduct.id}`, {
|
const updateRes = await fetch(`${API_BASE}/products/${selectedProduct.id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
categoryId: selectedProduct.categoryId,
|
categoryId: selectedProduct.categoryId,
|
||||||
options: selectedProduct.options,
|
options: selectedProduct.options,
|
||||||
@@ -302,7 +303,7 @@ export default function ProductsPage() {
|
|||||||
const updatedImages = (selectedProduct.images || []).filter(img => img.url !== imgUrl)
|
const updatedImages = (selectedProduct.images || []).filter(img => img.url !== imgUrl)
|
||||||
const updateRes = await fetch(`${API_BASE}/products/${selectedProduct.id}`, {
|
const updateRes = await fetch(`${API_BASE}/products/${selectedProduct.id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
categoryId: selectedProduct.categoryId,
|
categoryId: selectedProduct.categoryId,
|
||||||
options: selectedProduct.options,
|
options: selectedProduct.options,
|
||||||
@@ -367,7 +368,10 @@ export default function ProductsPage() {
|
|||||||
const handleDelete = async (id: number) => {
|
const handleDelete = async (id: number) => {
|
||||||
if (!confirm(t('common.confirmDelete', 'Are you sure you want to delete this product?'))) return
|
if (!confirm(t('common.confirmDelete', 'Are you sure you want to delete this product?'))) return
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/products/${id}`, { method: 'DELETE' })
|
const res = await fetch(`${API_BASE}/products/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
})
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
setSelectedIds(prev => prev.filter(item => item !== id))
|
setSelectedIds(prev => prev.filter(item => item !== id))
|
||||||
fetchData()
|
fetchData()
|
||||||
@@ -389,7 +393,7 @@ export default function ProductsPage() {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/products/bulk-delete`, {
|
const res = await fetch(`${API_BASE}/products/bulk-delete`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({ ids: selectedIds })
|
body: JSON.stringify({ ids: selectedIds })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { API_BASE } from '@homaroma/types'
|
import { API_BASE } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import { Plus, Edit2, Trash2, X } from 'lucide-react'
|
import { Plus, Edit2, Trash2, X } from 'lucide-react'
|
||||||
|
|
||||||
interface Promotion {
|
interface Promotion {
|
||||||
@@ -94,7 +95,7 @@ export default function PromotionsPage() {
|
|||||||
const method = editingId ? 'PUT' : 'POST'
|
const method = editingId ? 'PUT' : 'POST'
|
||||||
await fetch(url, {
|
await fetch(url, {
|
||||||
method,
|
method,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify(payload)
|
body: JSON.stringify(payload)
|
||||||
})
|
})
|
||||||
setIsModalOpen(false)
|
setIsModalOpen(false)
|
||||||
@@ -107,7 +108,10 @@ export default function PromotionsPage() {
|
|||||||
const handleDelete = async (id: number) => {
|
const handleDelete = async (id: number) => {
|
||||||
if (!confirm('Are you sure you want to delete this promotion?')) return
|
if (!confirm('Are you sure you want to delete this promotion?')) return
|
||||||
try {
|
try {
|
||||||
await fetch(`${API_BASE}/promotions/${id}`, { method: 'DELETE' })
|
await fetch(`${API_BASE}/promotions/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
})
|
||||||
fetchPromotions()
|
fetchPromotions()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
AlertCircle
|
AlertCircle
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { API_BASE } from '@homaroma/types';
|
import { API_BASE } from '@homaroma/types';
|
||||||
|
import { getAuthHeaders } from '../utils/auth';
|
||||||
|
|
||||||
const COMMON_UNITS = [
|
const COMMON_UNITS = [
|
||||||
{ label: 'ชิ้น (piece)', value: 'piece' },
|
{ label: 'ชิ้น (piece)', value: 'piece' },
|
||||||
@@ -46,7 +47,9 @@ export default function RawMaterialDetailPage() {
|
|||||||
if (isNew) return;
|
if (isNew) return;
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
fetch(`${API_BASE}/raw-materials/${id}`)
|
fetch(`${API_BASE}/raw-materials/${id}`, {
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
})
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
if (!res.ok) throw new Error('ไม่พบข้อมูลวัตถุดิบ');
|
if (!res.ok) throw new Error('ไม่พบข้อมูลวัตถุดิบ');
|
||||||
return await res.json();
|
return await res.json();
|
||||||
@@ -97,7 +100,7 @@ export default function RawMaterialDetailPage() {
|
|||||||
|
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
method,
|
method,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -127,6 +130,7 @@ export default function RawMaterialDetailPage() {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/raw-materials/${id}`, {
|
const res = await fetch(`${API_BASE}/raw-materials/${id}`, {
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
Pencil,
|
Pencil,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { API_BASE, type RawMaterial } from '@homaroma/types';
|
import { API_BASE, type RawMaterial } from '@homaroma/types';
|
||||||
|
import { getAuthHeaders } from '../utils/auth';
|
||||||
import RawMaterialBulkJsonModal from '../components/RawMaterialBulkJsonModal';
|
import RawMaterialBulkJsonModal from '../components/RawMaterialBulkJsonModal';
|
||||||
|
|
||||||
export default function RawMaterialsPage() {
|
export default function RawMaterialsPage() {
|
||||||
@@ -51,7 +52,7 @@ export default function RawMaterialsPage() {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/raw-materials/${item.id}`, {
|
const res = await fetch(`${API_BASE}/raw-materials/${item.id}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
pricePerUnit: val,
|
pricePerUnit: val,
|
||||||
}),
|
}),
|
||||||
@@ -78,7 +79,9 @@ export default function RawMaterialsPage() {
|
|||||||
const fetchMaterials = async () => {
|
const fetchMaterials = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/raw-materials`);
|
const res = await fetch(`${API_BASE}/raw-materials`, {
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
setMaterials(await res.json());
|
setMaterials(await res.json());
|
||||||
}
|
}
|
||||||
@@ -97,7 +100,10 @@ export default function RawMaterialsPage() {
|
|||||||
if (!confirm(`คุณแน่ใจหรือไม่ว่าต้องการลบวัตถุดิบ "${name}"?`)) return;
|
if (!confirm(`คุณแน่ใจหรือไม่ว่าต้องการลบวัตถุดิบ "${name}"?`)) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/raw-materials/${id}`, { method: 'DELETE' });
|
const res = await fetch(`${API_BASE}/raw-materials/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
fetchMaterials();
|
fetchMaterials();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { API_BASE, type StoreSettings, type StoreSettingsTranslation } from '@homaroma/types';
|
import { API_BASE, type StoreSettings, type StoreSettingsTranslation } from '@homaroma/types';
|
||||||
|
import { getAuthHeaders } from '../utils/auth';
|
||||||
import {
|
import {
|
||||||
Save,
|
Save,
|
||||||
Loader2,
|
Loader2,
|
||||||
@@ -257,7 +258,7 @@ export default function StoreSettingsPage() {
|
|||||||
|
|
||||||
const res = await fetch(`${API_BASE}/settings`, {
|
const res = await fetch(`${API_BASE}/settings`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect, useRef } from 'react'
|
import { useState, useEffect, useRef } from 'react'
|
||||||
import { API_BASE, type StoreSettings } from '@homaroma/types'
|
import { API_BASE, type StoreSettings } from '@homaroma/types'
|
||||||
|
import { getAuthHeaders } from '../utils/auth'
|
||||||
import ThemeLivePreview from '../components/ThemeLivePreview'
|
import ThemeLivePreview from '../components/ThemeLivePreview'
|
||||||
import {
|
import {
|
||||||
Save,
|
Save,
|
||||||
@@ -492,7 +493,7 @@ export default function ThemeSettingsPage() {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/settings`, {
|
const res = await fetch(`${API_BASE}/settings`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: getAuthHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
themeTokens: {
|
themeTokens: {
|
||||||
...themeTokens,
|
...themeTokens,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
SlidersHorizontal,
|
SlidersHorizontal,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { API_BASE } from '@homaroma/types';
|
import { API_BASE } from '@homaroma/types';
|
||||||
|
import { getAuthHeaders } from '../../utils/auth';
|
||||||
import ProfitabilityHeaderTabs from './ProfitabilityHeaderTabs';
|
import ProfitabilityHeaderTabs from './ProfitabilityHeaderTabs';
|
||||||
|
|
||||||
function fmtMoney(val: number | null | undefined): string {
|
function fmtMoney(val: number | null | undefined): string {
|
||||||
@@ -110,7 +111,9 @@ export default function CostProfitabilityMatrixPage() {
|
|||||||
sortBy,
|
sortBy,
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await fetch(`${API_BASE}/profitability/matrix?${params.toString()}`);
|
const res = await fetch(`${API_BASE}/profitability/matrix?${params.toString()}`, {
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
});
|
||||||
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
|
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
setData(json);
|
setData(json);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
TrendingUp,
|
TrendingUp,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { API_BASE } from '@homaroma/types';
|
import { API_BASE } from '@homaroma/types';
|
||||||
|
import { getAuthHeaders } from '../../utils/auth';
|
||||||
import ProfitabilityHeaderTabs from './ProfitabilityHeaderTabs';
|
import ProfitabilityHeaderTabs from './ProfitabilityHeaderTabs';
|
||||||
import DateRangeSelector, { type DateFilterState } from './DateRangeSelector';
|
import DateRangeSelector, { type DateFilterState } from './DateRangeSelector';
|
||||||
|
|
||||||
@@ -92,7 +93,9 @@ export default function MrpPlanningPage() {
|
|||||||
...(dateFilter.endDate ? { endDate: dateFilter.endDate } : {}),
|
...(dateFilter.endDate ? { endDate: dateFilter.endDate } : {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await fetch(`${API_BASE}/profitability/mrp?${params.toString()}`);
|
const res = await fetch(`${API_BASE}/profitability/mrp?${params.toString()}`, {
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
});
|
||||||
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
|
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
setData(json);
|
setData(json);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { API_BASE } from '@homaroma/types';
|
import { API_BASE } from '@homaroma/types';
|
||||||
|
import { getAuthHeaders } from '../../utils/auth';
|
||||||
import ProfitabilityHeaderTabs from './ProfitabilityHeaderTabs';
|
import ProfitabilityHeaderTabs from './ProfitabilityHeaderTabs';
|
||||||
import DateRangeSelector, { type DateFilterState } from './DateRangeSelector';
|
import DateRangeSelector, { type DateFilterState } from './DateRangeSelector';
|
||||||
|
|
||||||
@@ -121,7 +122,9 @@ export default function OmnichannelMarginPage() {
|
|||||||
...(dateFilter.endDate ? { endDate: dateFilter.endDate } : {}),
|
...(dateFilter.endDate ? { endDate: dateFilter.endDate } : {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await fetch(`${API_BASE}/profitability/omnichannel?${params.toString()}`);
|
const res = await fetch(`${API_BASE}/profitability/omnichannel?${params.toString()}`, {
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
});
|
||||||
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
|
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
setData(json);
|
setData(json);
|
||||||
|
|||||||
Reference in new issue
Block a user