Komponen Modal
Kami menyediakan koleksi komponen Modal yang dibangun dengan Mantine UI. Komponen-komponen ini dapat digunakan kembali di berbagai tempat di aplikasi untuk berbagai keperluan seperti form, konfirmasi aksi, upload file, dan preview file.
Instalasi Dependencies
Sebelum menggunakan komponen Modal, pastikan dependencies berikut sudah terinstal:
npm install @mantine/core @mantine/hooks @mantine/dropzone @mantine/carousel @tabler/icons-react
Struktur Komponen
components/Modal/
├── ModalIndex.tsx # Modal dasar untuk form
├── ModalConfirm.tsx # Modal konfirmasi dengan tombol Ya/Tidak
├── ModalWarning.tsx # Modal peringatan
├── ModalFileUpload.tsx # Modal upload file
├── ModalFileViewer.tsx # Modal preview file tunggal
├── ModalFileViewerMultiple.tsx # Modal preview file dengan tabs
└── ModalDownloadDocument.tsx # Modal download dengan multiple tombol
ModalIndex
Modal dasar untuk form input dengan tombol simpan dan batal. Gunakan komponen ini ketika Anda perlu input data dari user.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | ✅ | - | Judul modal |
opened | boolean | ✅ | - | Status modal terbuka/tertutup |
onClose | () => void | ✅ | - | Callback saat modal ditutup |
handleSubmit | () => void | ✅ | - | Callback saat form disubmit |
children | ReactNode | ✅ | - | Konten form di dalam modal |
isLoading | boolean | ❌ | false | Status loading untuk disable tombol |
Contoh Penggunaan
import { useState } from 'react';
import { TextInput } from '@mantine/core';
import ModalIndex from '@/components/Modal/ModalIndex';
function MyComponent() {
const [opened, setOpened] = useState(false);
const [formData, setFormData] = useState({ name: '', email: '' });
const [loading, setLoading] = useState(false);
const handleSubmit = async () => {
setLoading(true);
try {
// Proses simpan data
await saveData(formData);
setOpened(false);
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
return (
<>
<button onClick={() => setOpened(true)}>Tambah Data</button>
<ModalIndex
title="Tambah Data Baru"
opened={opened}
onClose={() => setOpened(false)}
handleSubmit={handleSubmit}
isLoading={loading}
>
<TextInput
label="Nama"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
required
/>
<TextInput
label="Email"
type="email"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
required
/>
</ModalIndex>
</>
);
}
ModalConfirm
Modal konfirmasi dengan tombol Ya/Tidak dan icon. Gunakan komponen ini ketika Anda ingin user melakukan konfirmasi sebelum melakukan aksi penting.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | ✅ | - | Judul modal |
opened | boolean | ✅ | - | Status modal terbuka/tertutup |
onClose | () => void | ✅ | - | Callback saat modal ditutup (tombol Tidak) |
handleSubmit | () => void | ✅ | - | Callback saat tombol Ya diklik |
children | ReactNode | ✅ | - | Konten konfirmasi |
isLoading | boolean | ❌ | false | Status loading |
buttonColor | string | ❌ | 'blue' | Warna tombol Ya |
Contoh Penggunaan
import { useState } from 'react';
import { Group, Text } from '@mantine/core';
import { IconInfoCircle } from '@tabler/icons-react';
import ModalConfirm from '@/components/Modal/ModalConfirm';
function DeleteUserComponent({ userId, userName }) {
const [opened, setOpened] = useState(false);
const [loading, setLoading] = useState(false);
const handleDelete = async () => {
setLoading(true);
try {
await deleteUser(userId);
setOpened(false);
// Refresh data atau navigasi
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
return (
<>
<button onClick={() => setOpened(true)}>Hapus</button>
<ModalConfirm
title="Konfirmasi Penghapusan"
opened={opened}
onClose={() => setOpened(false)}
handleSubmit={handleDelete}
isLoading={loading}
buttonColor="red"
>
<Group justify="center">
<IconInfoCircle color="red" size={48} />
<Text ta="center">
Apakah Anda yakin ingin menghapus user "{userName}"?
<br />
Tindakan ini tidak dapat dibatalkan.
</Text>
</Group>
</ModalConfirm>
</>
);
}
Variasi Penggunaan
// Konfirmasi Persetujuan
<ModalConfirm
title="Setujui Permohonan"
opened={opened}
onClose={() => setOpened(false)}
handleSubmit={handleApprove}
buttonColor="green"
>
<Text ta="center">
Apakah Anda yakin ingin menyetujui permohonan ini?
</Text>
</ModalConfirm>
// Konfirmasi Penolakan
<ModalConfirm
title="Tolak Permohonan"
opened={opened}
onClose={() => setOpened(false)}
handleSubmit={handleReject}
buttonColor="red"
>
<Text ta="center">
Apakah Anda yakin ingin menolak permohonan ini?
</Text>
</ModalConfirm>
ModalWarning
Modal peringatan dengan icon warning untuk menampilkan pesan penting. Modal ini hanya memiliki satu tombol untuk menutup.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | ✅ | - | Judul peringatan |
opened | boolean | ✅ | - | Status modal terbuka/tertutup |
onClose | () => void | ✅ | - | Callback saat modal ditutup |
children | ReactNode | ❌ | - | Konten tambahan (opsional) |
isLoading | boolean | ❌ | false | Status loading |
Contoh Penggunaan
import { useState } from 'react';
import ModalWarning from '@/components/Modal/ModalWarning';
function MyComponent() {
const [showWarning, setShowWarning] = useState(false);
const handleAction = () => {
// Cek kondisi
if (someConditionNotMet) {
setShowWarning(true);
return;
}
// Lanjutkan aksi normal
};
return (
<>
<button onClick={handleAction}>Submit</button>
<ModalWarning
title="Peringatan!"
opened={showWarning}
onClose={() => setShowWarning(false)}
>
Mohon lengkapi semua data yang diperlukan sebelum melanjutkan.
</ModalWarning>
</>
);
}
Use Cases
// Peringatan validasi
<ModalWarning
title="Data Tidak Lengkap"
opened={opened}
onClose={() => setOpened(false)}
>
Harap isi semua field yang wajib diisi (ditandai dengan *)
</ModalWarning>
// Peringatan akses ditolak
<ModalWarning
title="Akses Ditolak"
opened={opened}
onClose={() => setOpened(false)}
>
Anda tidak memiliki izin untuk mengakses fitur ini.
</ModalWarning>
// Peringatan session expired
<ModalWarning
title="Sesi Berakhir"
opened={opened}
onClose={handleLogout}
>
Sesi Anda telah berakhir. Silakan login kembali.
</ModalWarning>
ModalFileUpload
Modal untuk upload file dengan fitur drag-and-drop. Komponen ini juga mendukung kompresi gambar otomatis dan upload multiple files.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
opened | boolean | ✅ | - | Status modal terbuka/tertutup |
onClose | () => void | ✅ | - | Callback saat modal ditutup |
onSubmit | () => void | ✅ | - | Callback saat submit |
onFileSet | (files: File | File[]) => void | ✅ | - | Callback saat file di-set |
token | string | ✅ | - | Token autentikasi untuk delete file |
onDeleteFileLinkByIndex | (index: number) => void | ❌ | - | Callback untuk hapus file by index |
multipleUpload | boolean | ❌ | false | Aktifkan upload multiple files |
maxFileSize | number | ❌ | 5 | Ukuran maksimal file (MB) |
acceptedFileTypes | string[] | ❌ | ['application/pdf', 'image/png', 'image/jpeg', 'image/jpg'] | Tipe file yang diterima |
title | string | ❌ | 'Upload File' | Judul modal |
uploadedFiles | string[] | ❌ | [] | Array file path yang sudah diupload |
enableImageCompression | boolean | ❌ | true | Aktifkan kompresi gambar otomatis |
compressionOptions | object | ❌ | See below | Opsi kompresi gambar |
isLoading | boolean | ❌ | false | Status loading |
Compression Options Default
{
maxWidth: 1920,
maxHeight: 1080,
quality: 0.8,
maxSizeKB: 1024
}
Contoh Penggunaan - Single File
import { useState } from 'react';
import ModalFileUpload from '@/components/Modal/ModalFileUpload';
import { useAuth } from '@/hooks/useAuth';
function UploadPhotoComponent() {
const [opened, setOpened] = useState(false);
const [file, setFile] = useState<File | null>(null);
const { token } = useAuth();
const handleSubmit = async () => {
if (!file) return;
const formData = new FormData();
formData.append('file', file);
try {
await uploadPhoto(formData);
setOpened(false);
setFile(null);
} catch (error) {
console.error(error);
}
};
return (
<>
<button onClick={() => setOpened(true)}>Upload Foto</button>
<ModalFileUpload
opened={opened}
onClose={() => setOpened(false)}
onSubmit={handleSubmit}
onFileSet={(selectedFile) => setFile(selectedFile as File)}
token={token}
title="Upload Foto Profil"
maxFileSize={2}
acceptedFileTypes={['image/png', 'image/jpeg', 'image/jpg']}
enableImageCompression={true}
compressionOptions={{
maxWidth: 800,
maxHeight: 800,
quality: 0.8,
maxSizeKB: 500,
}}
/>
</>
);
}
Contoh Penggunaan - Multiple Files
import { useState } from 'react';
import ModalFileUpload from '@/components/Modal/ModalFileUpload';
function UploadDocumentsComponent() {
const [opened, setOpened] = useState(false);
const [files, setFiles] = useState<File[]>([]);
const [uploadedPaths, setUploadedPaths] = useState<string[]>([]);
const { token } = useAuth();
const handleSubmit = async () => {
if (files.length === 0) return;
const formData = new FormData();
files.forEach((file) => {
formData.append('files', file);
});
try {
const response = await uploadDocuments(formData);
setUploadedPaths(response.filePaths);
setOpened(false);
} catch (error) {
console.error(error);
}
};
const handleDeleteFile = (index: number) => {
setFiles((prev) => prev.filter((_, idx) => idx !== index));
};
return (
<>
<button onClick={() => setOpened(true)}>Upload Dokumen</button>
<ModalFileUpload
opened={opened}
onClose={() => setOpened(false)}
onSubmit={handleSubmit}
onFileSet={(selectedFiles) => setFiles(selectedFiles as File[])}
onDeleteFileLinkByIndex={handleDeleteFile}
token={token}
title="Upload Dokumen Pendukung"
multipleUpload={true}
maxFileSize={10}
acceptedFileTypes={[
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'image/png',
'image/jpeg',
]}
uploadedFiles={uploadedPaths}
enableImageCompression={true}
/>
</>
);
}
Fitur Utama
- Drag and drop interface
- Kompresi gambar otomatis
- Preview file sebelum upload
- Support multiple files
- Validasi tipe dan ukuran file
- Progress indicator
- Statistik kompresi (original vs compressed size)
ModalFileViewer
Modal untuk preview file seperti gambar, PDF, dan video. Komponen ini mendukung view satu file atau multiple files dengan carousel navigation.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
opened | boolean | ✅ | - | Status modal terbuka/tertutup |
onClose | () => void | ✅ | - | Callback saat modal ditutup |
filePath | string | string[] | ✅ | - | Path file atau array path |
title | string | ❌ | 'File Preview' | Judul modal |
downloadFileName | string | ❌ | - | Nama file saat didownload |
downloadable | boolean | ❌ | true | Aktifkan tombol download |
modalProps | ModalProps | ❌ | - | Props tambahan untuk Mantine Modal |
initialSlide | number | ❌ | 0 | Index slide awal (untuk multiple files) |
Contoh Penggunaan - Single File
import { useState } from 'react';
import ModalFileViewer from '@/components/Modal/ModalFileViewer';
function DocumentViewComponent({ documentPath }) {
const [opened, setOpened] = useState(false);
return (
<>
<button onClick={() => setOpened(true)}>Lihat Dokumen</button>
<ModalFileViewer
opened={opened}
onClose={() => setOpened(false)}
filePath={documentPath}
title="Dokumen Permohonan"
downloadFileName="permohonan.pdf"
downloadable={true}
/>
</>
);
}
Contoh Penggunaan - Multiple Files dengan Carousel
import { useState } from 'react';
import ModalFileViewer from '@/components/Modal/ModalFileViewer';
function GalleryComponent({ imagePaths }) {
const [opened, setOpened] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(0);
return (
<>
<div className="gallery">
{imagePaths.map((path, index) => (
<img
key={index}
src={`${process.env.NEXT_PUBLIC_FILE_HOST}file/${path}`}
alt={`Image ${index + 1}`}
onClick={() => {
setSelectedIndex(index);
setOpened(true);
}}
style={{ cursor: 'pointer', width: 100, height: 100 }}
/>
))}
</div>
<ModalFileViewer
opened={opened}
onClose={() => setOpened(false)}
filePath={imagePaths}
title="Galeri Foto"
downloadable={true}
initialSlide={selectedIndex}
/>
</>
);
}
Supported File Types
| Type | Extensions | Preview |
|---|---|---|
| Images | .jpg, .jpeg, .png, .gif, .bmp, .webp, .svg | ✅ Full preview |
.pdf | ✅ Embedded viewer | |
| Video | .mp4 | ✅ Video player |
| Others | All other types | ⚠️ Shows "not supported" message |
ModalFileViewerMultiple
Modal khusus untuk preview multiple files dengan navigasi berbasis tabs. Setiap file ditampilkan di tab terpisah dengan nama file yang terlihat.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
opened | boolean | ✅ | - | Status modal terbuka/tertutup |
onClose | () => void | ✅ | - | Callback saat modal ditutup |
filePath | string[] | ✅ | - | Array path files |
title | string | ❌ | 'File Preview' | Judul modal |
downloadFileName | string | ❌ | - | Nama file saat didownload |
downloadable | boolean | ❌ | true | Aktifkan tombol download |
modalProps | ModalProps | ❌ | - | Props tambahan untuk Mantine Modal |
Contoh Penggunaan
import { useState } from 'react';
import ModalFileViewerMultiple from '@/components/Modal/ModalFileViewerMultiple';
function MultipleDocumentsComponent({ documents }) {
const [opened, setOpened] = useState(false);
return (
<>
<button onClick={() => setOpened(true)}>
Lihat Semua Dokumen ({documents.length})
</button>
<ModalFileViewerMultiple
opened={opened}
onClose={() => setOpened(false)}
filePath={documents.map(doc => doc.path)}
title="Dokumen Pendukung"
downloadable={true}
/>
</>
);
}
Perbedaan dengan ModalFileViewer
| Feature | ModalFileViewer | ModalFileViewerMultiple |
|---|---|---|
| Navigation | Carousel (swipe/arrow) | Tabs (click) |
| UI | Minimalis | Tab-based navigation |
| Best for | Image galleries | Documents with names |
| File names | Hidden | Shown in tabs |
ModalDownloadDocument
Modal dengan beberapa tombol download untuk berbagai format dokumen. Gunakan komponen ini ketika user bisa memilih format download yang mereka inginkan.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
opened | boolean | ✅ | - | Status modal terbuka/tertutup |
onClose | () => void | ✅ | - | Callback saat modal ditutup |
title | string | ✅ | - | Judul modal |
buttons | ModalButtonsProps[] | ✅ | - | Array konfigurasi tombol |
ModalButtonsProps
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
label | string | ✅ | - | Label tombol |
onClick | () => void | ✅ | - | Callback saat tombol diklik |
color | string | ❌ | 'blue' | Warna tombol |
show | boolean | ❌ | true | Tampilkan atau sembunyikan tombol |
loading | boolean | ❌ | false | Status loading tombol |
disabled | boolean | ❌ | false | Disable tombol |
tooltip | string | ❌ | - | Tooltip saat hover |
Contoh Penggunaan
import { useState } from 'react';
import ModalDownloadDocument from '@/components/Modal/ModalDownloadDocument';
function ReportDownloadComponent({ reportId }) {
const [opened, setOpened] = useState(false);
const [loading, setLoading] = useState({
pdf: false,
excel: false,
word: false,
});
const handleDownload = async (format: 'pdf' | 'excel' | 'word') => {
setLoading({ ...loading, [format]: true });
try {
const blob = await downloadReport(reportId, format);
// Trigger download
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `report-${reportId}.${format}`;
link.click();
window.URL.revokeObjectURL(url);
} catch (error) {
console.error(error);
} finally {
setLoading({ ...loading, [format]: false });
}
};
const buttons = [
{
label: 'Download PDF',
onClick: () => handleDownload('pdf'),
color: 'red',
loading: loading.pdf,
tooltip: 'Download laporan dalam format PDF',
},
{
label: 'Download Excel',
onClick: () => handleDownload('excel'),
color: 'green',
loading: loading.excel,
tooltip: 'Download laporan dalam format Excel',
},
{
label: 'Download Word',
onClick: () => handleDownload('word'),
color: 'blue',
loading: loading.word,
show: true, // Conditional show
tooltip: 'Download laporan dalam format Word',
},
];
return (
<>
<button onClick={() => setOpened(true)}>Download Laporan</button>
<ModalDownloadDocument
opened={opened}
onClose={() => setOpened(false)}
title="Pilih Format Download"
buttons={buttons}
/>
</>
);
}
Contoh dengan Conditional Buttons
const buttons = [
{
label: 'Download Format A',
onClick: handleDownloadA,
color: 'blue',
show: userData.role === 'admin', // Hanya tampil untuk admin
},
{
label: 'Download Format B',
onClick: handleDownloadB,
color: 'green',
show: true,
},
{
label: 'Download Format C',
onClick: handleDownloadC,
color: 'orange',
disabled: !dataAvailable, // Disable jika data tidak tersedia
tooltip: dataAvailable ? 'Download sekarang' : 'Data belum tersedia',
},
];
Styling & Customization
Semua komponen ini mendukung props tambahan dari Mantine Modal untuk customization lebih lanjut.
Override Mantine Modal Props
<ModalFileViewer
// ... props lainnya
modalProps={{
size: 'xl',
centered: false,
overlayProps: {
opacity: 0.55,
blur: 3,
},
transitionProps: {
transition: 'fade',
duration: 200,
},
}}
/>
Custom Styling dengan Mantine Theme
import { MantineProvider } from '@mantine/core';
function App() {
return (
<MantineProvider
theme={{
components: {
Modal: {
styles: {
title: {
fontWeight: 700,
fontSize: '1.25rem',
},
body: {
padding: '1.5rem',
},
},
},
},
}}
>
{/* Your app */}
</MantineProvider>
);
}
Helper Functions
Fungsi-fungsi utility untuk membantu pekerjaan dengan modal, terutama untuk kompresi gambar.
// utils/imageCompression.ts
export const compressImages = async (
files: File[],
options: {
maxWidth?: number;
maxHeight?: number;
quality?: number;
maxSizeKB?: number;
}
): Promise<File[]> => {
// Implementation
};
export const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
};
export const getSizeReduction = (original: number, compressed: number): string => {
const reduction = ((original - compressed) / original) * 100;
return `${reduction.toFixed(1)}%`;
};
Best Practices
Beberapa praktik terbaik saat menggunakan komponen Modal dalam aplikasi Anda.
Best Practices
Beberapa praktik terbaik saat menggunakan komponen Modal dalam aplikasi Anda.
Pisahkan state untuk setiap modal agar lebih mudah dikelola. Jangan gunakan satu state untuk multiple modals:
// ✅ Baik
const [openUpload, setOpenUpload] = useState(false);
const [openConfirm, setOpenConfirm] = useState(false);
const [openView, setOpenView] = useState(false);
// ❌ Hindari
const [modalType, setModalType] = useState<'upload' | 'confirm' | 'view' | null>(null);
Error Handling
Saat error terjadi, jangan langsung tutup modal. Biarkan user melihat error message dan bisa mencoba lagi:
const handleSubmit = async () => {
setLoading(true);
try {
await submitData();
setOpened(false);
// Tampilkan notifikasi sukses
} catch (error) {
console.error(error);
// Tampilkan error notification tapi jangan tutup modal
// User bisa langsung retry
} finally {
setLoading(false);
}
};
Clean Up
Selalu reset form state saat menutup modal:
const handleClose = () => {
// Reset form state
setFormData(initialState);
setErrors({});
// Tutup modal
setOpened(false);
};
Accessibility
Gunakan judul yang jelas dan deskriptif untuk setiap modal.
Loading States
Gunakan state loading untuk disable button dan prevent double submit.
Testing
Unit Test dengan Jest & React Testing Library
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { MantineProvider } from '@mantine/core';
import ModalConfirm from '@/components/Modal/ModalConfirm';
describe('ModalConfirm', () => {
const defaultProps = {
title: 'Konfirmasi',
opened: true,
onClose: jest.fn(),
handleSubmit: jest.fn(),
children: <div>Test content</div>,
};
const renderModal = (props = {}) => {
return render(
<MantineProvider>
<ModalConfirm {...defaultProps} {...props} />
</MantineProvider>
);
};
it('should render modal with title', () => {
renderModal();
expect(screen.getByText('Konfirmasi')).toBeInTheDocument();
});
it('should call onClose when clicking Tidak button', () => {
renderModal();
fireEvent.click(screen.getByText('Tidak'));
expect(defaultProps.onClose).toHaveBeenCalled();
});
it('should call handleSubmit when clicking Ya button', async () => {
renderModal();
fireEvent.click(screen.getByText('Ya'));
await waitFor(() => {
expect(defaultProps.handleSubmit).toHaveBeenCalled();
});
});
it('should show loading state on buttons', () => {
renderModal({ isLoading: true });
const buttons = screen.getAllByRole('button');
buttons.forEach(button => {
expect(button).toBeDisabled();
});
});
});
Migration ke Project Lain
Jika ingin menggunakan komponen Modal di project lain, ikuti langkah-langkah berikut.
Langkah 1: Copy Components
# Copy seluruh folder Modal
cp -r components/Modal /path/to/new-project/components/
Langkah 2: Install Dependencies
npm install @mantine/core @mantine/hooks @mantine/dropzone @mantine/carousel @tabler/icons-react
Langkah 3: Setup Utilities (untuk ModalFileUpload)
Jika menggunakan ModalFileUpload, copy juga utility functions:
cp utils/imageCompression.ts /path/to/new-project/utils/
Langkah 4: Setup Axios Instance
Sesuaikan axios configuration dengan project Anda:
// lib/axios/index.ts
export const axiosInstance = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL,
});
export const httpHeader = (token: string) => ({
headers: {
Authorization: `Bearer ${token}`,
},
});
Langkah 5: Environment Variables
Tambahkan ke file .env:
NEXT_PUBLIC_FILE_HOST=https://your-file-server.com/
NEXT_PUBLIC_API_URL=https://your-api.com/api/
Langkah 6: Sesuaikan Path Imports
Update path imports sesuai dengan struktur folder project Anda:
import ModalIndex from '@/components/Modal/ModalIndex';
📚 API Reference Summary
| Component | Primary Use Case | Key Feature |
|---|---|---|
ModalIndex | Form input | Basic form modal dengan submit/cancel |
ModalConfirm | User confirmation | Ya/Tidak buttons dengan icon |
ModalWarning | Alerts/Warnings | Single button dengan warning icon |
ModalFileUpload | File upload | Drag-drop, compression, multiple files |
ModalFileViewer | File preview | Image/PDF/Video viewer dengan carousel |
ModalFileViewerMultiple | Multiple docs | Tab-based file navigation |
ModalDownloadDocument | Download options | Multiple download buttons |
Troubleshooting
Modal tidak muncul
Pastikan Mantine Provider sudah setup di root aplikasi Anda:
import { MantineProvider } from '@mantine/core';
function App() {
return (
<MantineProvider>
{/* Your app */}
</MantineProvider>
);
}
File upload tidak bekerja
Periksa beberapa hal ini:
// 1. Environment variable sudah di-set
console.log(process.env.NEXT_PUBLIC_FILE_HOST);
// 2. Token valid
console.log(token);
// 3. File format support
Image compression gagal
Jika kompresi gambar gagal, component akan fallback ke file original. Pastikan:
- File adalah valid image
- Browser mendukung Canvas API
- File size tidak terlalu besar
TypeScript errors
Pastikan type definitions sudah terinstall:
npm install --save-dev @types/react @types/node
Referensi
Terakhir diupdate: January 2026
Versi: 1.0.0