Membangun aplikasi web di era modern memerlukan pondasi yang kokoh. Framework modern memberikan fleksibilitas luar biasa bagi developer untuk menghadirkan fitur kompleks secara cepat tanpa mengabaikan aspek keamanan dan keandalan sistem.
Tahapan Implementasi & Walkthrough Kode
Berikut adalah langkah demi langkah teknis yang dapat Anda ikuti langsung pada codebase Anda:
Tahap 1: Penulisan Kode & Konfigurasi
npm install clsx sweetalert2 @tabler/icons-react
Tahap 2: Penulisan Kode & Konfigurasi
import React from 'react'
export default function Input({label, type, className, errors, ...props}) {
return (
<div className='flex flex-col gap-2'>
<label className='text-gray-600 text-sm'>
{label}
</label>
<input
type={type}
className={`w-full px-4 py-2 border text-sm rounded-md focus:outline-none focus:ring-0 bg-white text-gray-700 focus:border-gray-200 border-gray-200 ${className}`}
{...props}
/>
{errors && (
<small className='text-xs text-red-500'>{errors}</small>
)}
</div>
)
}
Tahap 3: Penulisan Kode & Konfigurasi
import React from 'react'
Tahap 4: Penulisan Kode & Konfigurasi
export default function Input({label, type, className, errors, ...props})
Tahap 5: Penulisan Kode & Konfigurasi
export default function Checkbox({ label, ...props }) {
return (
<div>
<div className="flex flex-row items-center gap-2">
<input
{...props}
type="checkbox"
className={
"rounded-md bg-white border-gray-200 checked:bg-teal-500"
}
/>
<label className="text-sm text-gray-700">{label}</label>
</div>
</div>
);
}
Tahap 6: Penulisan Kode & Konfigurasi
export default function Container({children}) {
return (
<div className="py-12">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{children}
</div>
</div>
)
}
Tahap 7: Penulisan Kode & Konfigurasi
export default function Textarea({label, className, errors,...props}) {
return (
<div className='flex flex-col gap-2'>
<label className='text-gray-600 dark:text-gray-500 text-sm'>
{label}
</label>
<textarea
className={`w-full px-4 py-2 border text-sm rounded-md focus:outline-none focus:ring-0 bg-white text-gray-700 focus:border-gray-200 border-gray-200 ${className}`}
{...props}
/>
{errors && (
<small className='text-xs text-red-500'>{errors}</small>
)}
</div>
)
}
Tahap 8: Penulisan Kode & Konfigurasi
import { useForm } from '@inertiajs/react';
import { IconSearch } from '@tabler/icons-react';
import React from 'react'
export default function Search({url, placeholder}) {
// define use form inertia
const {data, setData, get} = useForm({
search : '',
})
// define method searchData
const handleSearchData = (e) => {
e.preventDefault();
get(`${url}?search=${data.search}`)
}
return (
<form onSubmit={handleSearchData}>
<div className='relative'>
<input
type='text'
value={data.search}
onChange={e => setData('search', e.target.value)}
className='py-2 px-4 pr-11 block w-full rounded-lg text-sm border focus:outline-none focus:ring-0 focus:ring-gray-400 text-gray-700 bg-white border-gray-200 focus:border-gray-200'
placeholder={placeholder}/>
<div className='absolute inset-y-0 right-0 flex items-center pointer-events-none pr-4'>
<IconSearch size={18} strokeWidth={1.5}/>
</div>
</div>
</form>
)
}
Tahap 9: Penulisan Kode & Konfigurasi
import { useForm } from '@inertiajs/react';
import { IconSearch } from '@tabler/icons-react';
import React from 'react'
Tahap 10: Penulisan Kode & Konfigurasi
export default function Search({url, placeholder})
Tahap 11: Penulisan Kode & Konfigurasi
const {data, setData, get} = useForm({
search : '',
})
Tahap 12: Penulisan Kode & Konfigurasi
// define method searchData
const handleSearchData = (e) => {
e.preventDefault();
get(`${url}?search=${data.search}`)
}
Tahap 13: Penulisan Kode & Konfigurasi
import React from 'react'
export default function Card({ title, children, className }) {
return (
<>
<div className={`p-4 rounded-t-lg border ${className} bg-white`}>
<div className='flex items-center gap-2 font-semibold text-sm text-gray-700 capitalize'>
{title}
</div>
</div>
<div className='bg-white p-4 border border-t-0 border-b rounded-b-lg'>
{children}
</div>
</>
)
}
Tahap 14: Penulisan Kode & Konfigurasi
import React from 'react'
const Card = ({ title, className, children }) => {
return (
<>
<div className={`p-4 rounded-t-lg border ${className} bg-white`}>
<div className='flex items-center gap-2 font-semibold text-sm text-gray-700 uppercase'>
{title}
</div>
</div>
<div className='bg-white rounded-b-lg border-t-0'>
{children}
</div>
</>
)
}
const Table = ({ children }) => {
return (
<div className="w-full overflow-hidden overflow-x-auto border-collapse rounded-b-lg border border-t-0">
<table className="w-full text-sm">
{children}
</table>
</div>
);
};
const Thead = ({ className, children }) => {
return (
<thead className={`${className} border-b bg-gray-50`}>{children}</thead>
);
};
const Tbody = ({ className, children }) => {
return (
<tbody className={`${className} divide-y bg-white`}>
{children}
</tbody>
);
};
const Td = ({ className, children}) => {
return (
<td
className={`${className} whitespace-nowrap p-4 align-middle text-gray-700`}
>
{children}
</td>
);
};
const Th = ({ className, children }) => {
return (
<th
scope="col"
className={`${className} h-12 px-4 text-left align-middle font-medium text-gray-700`}
>
{children}
</th>
);
};
const Empty = ({colSpan, message, children}) => {
return (
<tr>
<td colSpan={colSpan}>
<div className="flex items-center justify-center h-96">
<div className="text-center">
{children}
<div className="mt-5">
{message}
</div>
</div>
</div>
</td>
</tr>
)
}
Table.Card = Card;
Table.Thead = Thead;
Table.Tbody = Tbody;
Table.Td = Td;
Table.Th = Th;
Table.Empty = Empty;
export default Table;
Tahap 15: Penulisan Kode & Konfigurasi
import React from 'react'
import { Link } from '@inertiajs/react';
import { IconChevronRight, IconChevronLeft } from '@tabler/icons-react';
export default function Pagination({ links }) {
const style = 'p-1 text-sm border rounded-md bg-white text-gray-500 hover:bg-gray-100'
return (
<>
<ul className="mt-2 lg:mt-5 justify-end flex items-center gap-1">
{links.map((item, i) => {
return item.url != null ? (
item.label.includes('Previous') ? (
<Link className={style} key={i} href={item.url}>
<IconChevronLeft size={'20'} strokeWidth={'1.5'}/>
</Link>
) : item.label.includes('Next') ? (
<Link className={style} key={i} href={item.url}>
<IconChevronRight size={'20'} strokeWidth={'1.5'}/>
</Link>
) : (
<Link className={`px-2 py-1 text-sm border rounded-md text-gray-500 hover:bg-gray-100 ${item.active ? 'bg-white text-gray-700' : 'bg-white'}`} key={i} href={item.url}>
{item.label}
</Link>
)
) : null;
})}
</ul>
</>
)
}
Tahap 16: Penulisan Kode & Konfigurasi
import React from 'react'
import { Link } from '@inertiajs/react';
import { IconChevronRight, IconChevronLeft } from '@tabler/icons-react';
Tahap 17: Penulisan Kode & Konfigurasi
export default function Pagination({ links })
Tahap 18: Penulisan Kode & Konfigurasi
import { Link, useForm } from '@inertiajs/react'
import { IconArrowBack, IconCheck, IconPencilCog, IconPlus, IconTrash } from '@tabler/icons-react';
import React from 'react'
import Swal from 'sweetalert2';
export default function Button({ type, url, className, children, ...props }) {
const { delete : destroy } = useForm();
const handleDeleteData = async (url) => {
Swal.fire({
title: 'Are you sure you want to delete this?',
text: 'Data is unrecoverable!',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'Cancel'
}).then((result) => {
if (result.isConfirmed) {
destroy(url)
Swal.fire({
title: 'Success!',
text: 'Data deleted successfully!',
icon: 'success',
showConfirmButton: false,
timer: 1500
})
}
})
}
return (
<>
{type === 'add' &&
<Link href={url} className='px-4 py-2 text-sm border rounded-lg bg-white text-gray-700 flex items-center gap-2 hover:bg-gray-100'>
<IconPlus size={18} strokeWidth={1.5}/> <span className='hidden lg:flex'>Create New Data</span>
</Link>
}
{type === 'modal' &&
<button {...props} type='button' className={`${className} px-4 py-2 text-sm border rounded-lg flex items-center gap-2`}>
{children}
</button>
}
{type === 'submit' &&
<button type='submit' className='px-4 py-2 text-sm rounded-lg border border-teal-100 bg-teal-50 text-teal-500 flex items-center gap-2 hover:bg-teal-100'>
<IconCheck size={16} strokeWidth={1.5}/> Save Data
</button>
}
{type === 'cancel' &&
<Link href={url} className='px-4 py-2 text-sm rounded-lg border border-rose-100 bg-rose-50 text-rose-500 flex items-center gap-2 hover:bg-rose-100'>
<IconArrowBack size={16} strokeWidth={1.5}/> Go Back
</Link>
}
{type === 'edit' &&
<Link href={url} className='px-4 py-2 rounded-lg bg-orange-50 text-orange-500 flex items-center gap-2 hover:bg-orange-100'>
<IconPencilCog size={16} strokeWidth={1.5}/>
</Link>
}
{type === 'delete' &&
<button onClick={() => handleDeleteData(url)} className='px-4 py-2 rounded-lg bg-rose-50 text-rose-500 flex items-center gap-2 hover:bg-rose-100'>
<IconTrash size={18} strokeWidth={1.5}/>
</button>
}
</>
)
}
Tahap 19: Penulisan Kode & Konfigurasi
import { Link, useForm } from '@inertiajs/react'
import { IconArrowBack, IconCheck, IconPencilCog, IconPlus, IconTrash } from '@tabler/icons-react';
import React from 'react'
import Swal from 'sweetalert2';
Tahap 20: Penulisan Kode & Konfigurasi
export default function Button({ type, url, className, children, ...props })
Tahap 21: Penulisan Kode & Konfigurasi
const { delete : destroy } = useForm();
Tahap 22: Penulisan Kode & Konfigurasi
const handleDeleteData = async (url) => {
Swal.fire({
title: 'Are you sure you want to delete this?',
text: 'Data is unrecoverable!',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'Cancel'
}).then((result) => {
if (result.isConfirmed) {
destroy(url)
Swal.fire({
title: 'Success!',
text: 'Data deleted successfully!',
icon: 'success',
showConfirmButton: false,
timer: 1500
})
}
})
}
Tahap 23: Penulisan Kode & Konfigurasi
Swal.fire({
title: 'Are you sure you want to delete this?',
text: 'Data is unrecoverable!',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'Cancel'
})
Selalu gunakan pendekatan deklaratif, validasi input seawal mungkin di boundary layer, dan lakukan logging terstruktur untuk memudahkan observability ketika sistem dijalankan di lingkungan cloud produksi.
Kesimpulan
Dengan mengikuti langkah-langkah terstruktur di atas, implementasi modul Anda akan jauh lebih rapi, aman dari bug umum, serta siap diskalakan ke lingkungan produksi.
Nantikan artikel tutorial praktis lainnya seputar fullstack development, microservices, dan otomasi AI di Blog Engineering SiHubung. Selamat mencoba!