mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-24 15:25:46 +00:00
feat: konfigurasi sistem toggle pemakaian pakan ovk negatif
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
import { SystemConfigContent } from '@/figma-make/components/pages/master-data/system-config/SystemConfigContent';
|
||||||
|
|
||||||
|
const SystemConfigPage = () => {
|
||||||
|
return (
|
||||||
|
<section className='w-full'>
|
||||||
|
<SystemConfigContent />
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SystemConfigPage;
|
||||||
@@ -236,6 +236,7 @@ export const MAIN_DRAWER_LINKS: SidebarMenuItem[] = [
|
|||||||
'lti.master.uoms.list',
|
'lti.master.uoms.list',
|
||||||
'lti.master.warehouses.list',
|
'lti.master.warehouses.list',
|
||||||
'lti.master.production_standards.list',
|
'lti.master.production_standards.list',
|
||||||
|
'lti.system_settings.update',
|
||||||
],
|
],
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
@@ -303,6 +304,11 @@ export const MAIN_DRAWER_LINKS: SidebarMenuItem[] = [
|
|||||||
link: '/master-data/production-standard',
|
link: '/master-data/production-standard',
|
||||||
permission: ['lti.master.production_standards.list'],
|
permission: ['lti.master.production_standards.list'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: 'Konfigurasi Sistem',
|
||||||
|
link: '/master-data/system-config',
|
||||||
|
permission: ['lti.system_settings.update'],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|||||||
@@ -218,4 +218,6 @@ export const ROUTE_PERMISSIONS: Record<string, string[]> = {
|
|||||||
'/master-data/production-standard/detail/edit/': [
|
'/master-data/production-standard/detail/edit/': [
|
||||||
'lti.master.production_standards.update',
|
'lti.master.production_standards.update',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
'/master-data/system-config/': ['lti.system_settings.update'],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,176 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { Card, CardContent } from '@/figma-make/components/base/card';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
import useSWR from 'swr';
|
||||||
|
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||||
|
import { SystemSettingsApi } from '@/services/api/system-settings';
|
||||||
|
import { SystemSetting } from '@/types/api/system-settings/system-setting';
|
||||||
|
|
||||||
|
const ALLOW_NEGATIVE_PAKAN_OVK_KEY = 'allow_negative_pakan_ovk';
|
||||||
|
|
||||||
|
function SettingToggle({
|
||||||
|
setting,
|
||||||
|
onToggle,
|
||||||
|
loading,
|
||||||
|
}: {
|
||||||
|
setting: SystemSetting;
|
||||||
|
onToggle: (key: string, currentValue: boolean) => void;
|
||||||
|
loading: boolean;
|
||||||
|
}) {
|
||||||
|
const isEnabled = setting.value === 'true';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex items-start justify-between gap-4 py-5'>
|
||||||
|
<div className='flex-1'>
|
||||||
|
<p className='text-sm font-medium text-gray-900'>
|
||||||
|
{setting.key === ALLOW_NEGATIVE_PAKAN_OVK_KEY
|
||||||
|
? 'Mode Migrasi PAKAN & OVK'
|
||||||
|
: setting.key}
|
||||||
|
</p>
|
||||||
|
{setting.description && (
|
||||||
|
<p className='text-sm text-gray-500 mt-0.5'>{setting.description}</p>
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className={`inline-flex items-center mt-1.5 px-2 py-0.5 rounded-full text-xs font-medium ${
|
||||||
|
isEnabled
|
||||||
|
? 'bg-amber-100 text-amber-700'
|
||||||
|
: 'bg-gray-100 text-gray-600'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{isEnabled ? 'Aktif' : 'Nonaktif'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
role='switch'
|
||||||
|
aria-checked={isEnabled}
|
||||||
|
disabled={loading}
|
||||||
|
onClick={() => onToggle(setting.key, isEnabled)}
|
||||||
|
className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-[#0069e0] focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed ${
|
||||||
|
isEnabled ? 'bg-[#0069e0]' : 'bg-gray-200'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden='true'
|
||||||
|
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
|
||||||
|
isEnabled ? 'translate-x-5' : 'translate-x-0'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SystemConfigContent() {
|
||||||
|
const [toggling, setToggling] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: settingsResponse,
|
||||||
|
isLoading,
|
||||||
|
mutate: refreshSettings,
|
||||||
|
} = useSWR(SystemSettingsApi.basePath, SystemSettingsApi.getAllFetcher, {
|
||||||
|
keepPreviousData: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleToggle = async (key: string, currentValue: boolean) => {
|
||||||
|
if (key !== ALLOW_NEGATIVE_PAKAN_OVK_KEY) return;
|
||||||
|
|
||||||
|
setToggling(key);
|
||||||
|
try {
|
||||||
|
const res = await SystemSettingsApi.setAllowNegativePakanOvk({
|
||||||
|
value: !currentValue,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isResponseError(res)) {
|
||||||
|
toast.error(res.message || 'Gagal mengubah pengaturan');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await refreshSettings();
|
||||||
|
toast.success(
|
||||||
|
!currentValue
|
||||||
|
? 'Mode migrasi PAKAN & OVK diaktifkan'
|
||||||
|
: 'Mode migrasi PAKAN & OVK dinonaktifkan'
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
toast.error('Terjadi kesalahan saat mengubah pengaturan');
|
||||||
|
} finally {
|
||||||
|
setToggling(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const settings = isResponseSuccess(settingsResponse)
|
||||||
|
? settingsResponse.data
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (isLoading && !settingsResponse) {
|
||||||
|
return (
|
||||||
|
<div className='min-h-screen'>
|
||||||
|
<div className='p-6'>
|
||||||
|
<div className='mb-6'>
|
||||||
|
<h1 className='text-2xl font-semibold text-gray-900'>
|
||||||
|
Konfigurasi Sistem
|
||||||
|
</h1>
|
||||||
|
<p className='text-sm text-gray-600 mt-1'>
|
||||||
|
Master Data •{' '}
|
||||||
|
<span className='text-[#0069e0]'>Konfigurasi Sistem</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Card className='border-gray-200/60 shadow-sm rounded-xl bg-white'>
|
||||||
|
<CardContent className='p-12 text-center text-gray-500'>
|
||||||
|
Memuat data...
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='min-h-screen'>
|
||||||
|
<div className='p-6'>
|
||||||
|
<div className='mb-6'>
|
||||||
|
<h1 className='text-2xl font-semibold text-gray-900'>
|
||||||
|
Konfigurasi Sistem
|
||||||
|
</h1>
|
||||||
|
<p className='text-sm text-gray-600 mt-1'>
|
||||||
|
Master Data •{' '}
|
||||||
|
<span className='text-[#0069e0]'>Konfigurasi Sistem</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Card className='border-gray-200/60 shadow-sm rounded-xl bg-white'>
|
||||||
|
<CardContent className='p-0'>
|
||||||
|
<div className='px-6 py-4 border-b border-gray-200/60'>
|
||||||
|
<h2 className='text-base font-semibold text-gray-800'>
|
||||||
|
Pengaturan Global
|
||||||
|
</h2>
|
||||||
|
<p className='text-sm text-gray-500 mt-0.5'>
|
||||||
|
Pengaturan ini berlaku untuk seluruh sistem.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='px-6 divide-y divide-gray-200/60'>
|
||||||
|
{settings.length === 0 ? (
|
||||||
|
<p className='py-10 text-center text-sm text-gray-500'>
|
||||||
|
Tidak ada pengaturan tersedia.
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
settings.map((setting) => (
|
||||||
|
<SettingToggle
|
||||||
|
key={setting.key}
|
||||||
|
setting={setting}
|
||||||
|
onToggle={handleToggle}
|
||||||
|
loading={toggling === setting.key}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import { httpClient, httpClientFetcher } from '@/services/http/client';
|
||||||
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
|
import {
|
||||||
|
SetAllowNegativePakanOvkPayload,
|
||||||
|
SystemSetting,
|
||||||
|
} from '@/types/api/system-settings/system-setting';
|
||||||
|
|
||||||
|
const BASE_PATH = '/system-settings';
|
||||||
|
|
||||||
|
export const SystemSettingsApi = {
|
||||||
|
basePath: BASE_PATH,
|
||||||
|
|
||||||
|
getAllFetcher: (
|
||||||
|
endpoint: string
|
||||||
|
): Promise<BaseApiResponse<SystemSetting[]>> =>
|
||||||
|
httpClientFetcher<BaseApiResponse<SystemSetting[]>>(endpoint),
|
||||||
|
|
||||||
|
async getAll(): Promise<BaseApiResponse<SystemSetting[]> | undefined> {
|
||||||
|
try {
|
||||||
|
return await httpClient<BaseApiResponse<SystemSetting[]>>(BASE_PATH);
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError<BaseApiResponse<SystemSetting[]>>(error)) {
|
||||||
|
return error.response?.data;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async setAllowNegativePakanOvk(
|
||||||
|
payload: SetAllowNegativePakanOvkPayload
|
||||||
|
): Promise<BaseApiResponse | undefined> {
|
||||||
|
try {
|
||||||
|
return await httpClient<BaseApiResponse>(
|
||||||
|
`${BASE_PATH}/allow-negative-pakan-ovk`,
|
||||||
|
{
|
||||||
|
method: 'PATCH',
|
||||||
|
body: payload,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError<BaseApiResponse>(error)) {
|
||||||
|
return error.response?.data;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export type SystemSetting = {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
description: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SetAllowNegativePakanOvkPayload = {
|
||||||
|
value: boolean;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user