Fix logo/favicon upload, badge colors, and page title issues
Issue 1 - Logo/Favicon Upload: - Add preview before upload (user must confirm) - Show selected file preview with confirm/cancel buttons - Fallback to current image if preview cancelled - File size validation (2MB logo, 1MB favicon) - Add STORAGE_RLS_FIX.sql for storage policy setup Issue 2 - Badge Colors: - Already implemented correctly in all files - All "Lunas" badges use bg-brand-accent class - Verified: OrderDetail, MemberOrders, Dashboard, MemberDashboard Issue 3 - Page Title Error: - Change .single() to .maybeSingle() in useBranding hook - Handle error case gracefully with default branding - Set default title even when platform_settings is empty - This fixes the "JSON object requested, multiple (or no) rows returned" error 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,6 +54,10 @@ export function BrandingTab() {
|
||||
const [uploadingLogo, setUploadingLogo] = useState(false);
|
||||
const [uploadingFavicon, setUploadingFavicon] = useState(false);
|
||||
|
||||
// Preview states for selected files
|
||||
const [logoPreview, setLogoPreview] = useState<string | null>(null);
|
||||
const [faviconPreview, setFaviconPreview] = useState<string | null>(null);
|
||||
|
||||
const logoInputRef = useRef<HTMLInputElement>(null);
|
||||
const faviconInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -241,20 +245,64 @@ export function BrandingTab() {
|
||||
|
||||
const handleLogoSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) handleLogoUpload(file);
|
||||
if (!file) return;
|
||||
|
||||
// Validate file size (2MB max)
|
||||
if (file.size > 2 * 1024 * 1024) {
|
||||
toast({ title: 'Error', description: 'Ukuran file maksimal 2MB', variant: 'destructive' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Show preview first
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
setLogoPreview(reader.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
const handleFaviconSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) handleFaviconUpload(file);
|
||||
if (!file) return;
|
||||
|
||||
// Validate file size (1MB max)
|
||||
if (file.size > 1 * 1024 * 1024) {
|
||||
toast({ title: 'Error', description: 'Ukuran file maksimal 1MB', variant: 'destructive' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Show preview first
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => {
|
||||
setFaviconPreview(reader.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
};
|
||||
|
||||
const handleConfirmLogoUpload = async () => {
|
||||
const file = logoInputRef.current?.files?.[0];
|
||||
if (file) {
|
||||
await handleLogoUpload(file);
|
||||
setLogoPreview(null); // Clear preview after upload
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirmFaviconUpload = async () => {
|
||||
const file = faviconInputRef.current?.files?.[0];
|
||||
if (file) {
|
||||
await handleFaviconUpload(file);
|
||||
setFaviconPreview(null); // Clear preview after upload
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveLogo = () => {
|
||||
setSettings({ ...settings, brand_logo_url: '' });
|
||||
setLogoPreview(null);
|
||||
};
|
||||
|
||||
const handleRemoveFavicon = () => {
|
||||
setSettings({ ...settings, brand_favicon_url: '' });
|
||||
setFaviconPreview(null);
|
||||
};
|
||||
|
||||
if (loading) return <div className="animate-pulse h-64 bg-muted rounded-md" />;
|
||||
@@ -314,7 +362,39 @@ export function BrandingTab() {
|
||||
/>
|
||||
|
||||
<div className="space-y-2">
|
||||
{settings.brand_logo_url ? (
|
||||
{/* Show preview if file selected, otherwise show current logo or upload button */}
|
||||
{logoPreview ? (
|
||||
<div className="relative">
|
||||
<div className="p-4 bg-muted rounded-md flex items-center justify-center">
|
||||
<img
|
||||
src={logoPreview}
|
||||
alt="Logo preview"
|
||||
className="h-16 object-contain"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-2 mt-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="default"
|
||||
size="sm"
|
||||
onClick={handleConfirmLogoUpload}
|
||||
disabled={uploadingLogo}
|
||||
className="flex-1 border-2"
|
||||
>
|
||||
{uploadingLogo ? 'Mengupload...' : 'Konfirmasi Upload'}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setLogoPreview(null)}
|
||||
disabled={uploadingLogo}
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : settings.brand_logo_url ? (
|
||||
<div className="relative">
|
||||
<div className="p-4 bg-muted rounded-md flex items-center justify-center">
|
||||
<img
|
||||
@@ -384,7 +464,39 @@ export function BrandingTab() {
|
||||
/>
|
||||
|
||||
<div className="space-y-2">
|
||||
{settings.brand_favicon_url ? (
|
||||
{/* Show preview if file selected, otherwise show current favicon or upload button */}
|
||||
{faviconPreview ? (
|
||||
<div className="relative">
|
||||
<div className="p-4 bg-muted rounded-md flex items-center justify-center">
|
||||
<img
|
||||
src={faviconPreview}
|
||||
alt="Favicon preview"
|
||||
className="h-12 w-12 object-contain"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-2 mt-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="default"
|
||||
size="sm"
|
||||
onClick={handleConfirmFaviconUpload}
|
||||
disabled={uploadingFavicon}
|
||||
className="flex-1 border-2"
|
||||
>
|
||||
{uploadingFavicon ? 'Mengupload...' : 'Konfirmasi Upload'}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setFaviconPreview(null)}
|
||||
disabled={uploadingFavicon}
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : settings.brand_favicon_url ? (
|
||||
<div className="relative">
|
||||
<div className="p-4 bg-muted rounded-md flex items-center justify-center">
|
||||
<img
|
||||
|
||||
Reference in New Issue
Block a user