feat(setting): add setting saturday work #154
|
|
@ -3,6 +3,7 @@
|
||||||
namespace Modules\Admin\app\Http\Controllers;
|
namespace Modules\Admin\app\Http\Controllers;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Modules\Admin\app\Models\Category;
|
use Modules\Admin\app\Models\Category;
|
||||||
|
|
||||||
|
|
@ -29,4 +30,45 @@ class CategoryController extends Controller
|
||||||
$data = Category::where('c_type', '=', $type)->where('c_active', '=', 1)->select('id', 'c_code', 'c_name', 'c_value', 'c_type')->get();
|
$data = Category::where('c_type', '=', $type)->where('c_active', '=', 1)->select('id', 'c_code', 'c_name', 'c_value', 'c_type')->get();
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function workDays()
|
||||||
|
{
|
||||||
|
$saturday_work_schedules = Category::where('c_type', 'SATURDAY_WORK_SCHEDULE')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'data' => $saturday_work_schedules,
|
||||||
|
'status' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateWorkDays(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'c_code' => 'required|date_format:d-m-Y',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$schedule = Category::where('c_type', 'SATURDAY_WORK_SCHEDULE')->first();
|
||||||
|
|
||||||
|
if (!$schedule) {
|
||||||
|
$schedule = Category::create([
|
||||||
|
'c_type' => 'SATURDAY_WORK_SCHEDULE',
|
||||||
|
'c_name' => "Ngày bắt đầu làm việc thứ 7 trong năm",
|
||||||
|
'c_code' => $request->c_code,
|
||||||
|
'c_value' => Carbon::now()->year,
|
||||||
|
'c_active' => true,
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
$schedule->update([
|
||||||
|
'c_code' => $request->c_code,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => true,
|
||||||
|
'message' => 'Saturday work schedule updated successfully'
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,12 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
class Category extends Model
|
class Category extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'c_type',
|
||||||
|
'c_name',
|
||||||
|
'c_code',
|
||||||
|
'c_value',
|
||||||
|
'c_active',
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,8 @@ Route::middleware('api')
|
||||||
'prefix' => 'category',
|
'prefix' => 'category',
|
||||||
], function () {
|
], function () {
|
||||||
Route::get('/get-list-master', [CategoryController::class, 'getListMaster']);
|
Route::get('/get-list-master', [CategoryController::class, 'getListMaster']);
|
||||||
|
Route::get('/work-days', [CategoryController::class, 'workDays']);
|
||||||
|
Route::put('/update-work-days', [CategoryController::class, 'updateWorkDays']);
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::group([
|
Route::group([
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ export const updateWorkingDays =
|
||||||
|
|
||||||
//Category
|
//Category
|
||||||
export const getListMaster = API_URL + 'v1/admin/category/get-list-master'
|
export const getListMaster = API_URL + 'v1/admin/category/get-list-master'
|
||||||
|
export const getWorkDay = API_URL + 'v1/admin/category/work-days'
|
||||||
|
export const updateWorkDay = API_URL + 'v1/admin/category/update-work-days'
|
||||||
|
|
||||||
//LeaveManagement
|
//LeaveManagement
|
||||||
export const getLeaveManagement = API_URL + 'v1/admin/leave-management'
|
export const getLeaveManagement = API_URL + 'v1/admin/leave-management'
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,46 @@
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import {
|
import {
|
||||||
|
Alert,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
|
Code,
|
||||||
Dialog,
|
Dialog,
|
||||||
Flex,
|
Flex,
|
||||||
|
Grid,
|
||||||
Group,
|
Group,
|
||||||
Loader,
|
Loader,
|
||||||
|
LoadingOverlay,
|
||||||
Modal,
|
Modal,
|
||||||
|
Paper,
|
||||||
Select,
|
Select,
|
||||||
Tabs,
|
Tabs,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
|
Title,
|
||||||
} from '@mantine/core'
|
} from '@mantine/core'
|
||||||
import classes from './OrganizationSettings.module.css'
|
import classes from './OrganizationSettings.module.css'
|
||||||
import DataTableAll from '@/components/DataTable/DataTable'
|
import DataTableAll from '@/components/DataTable/DataTable'
|
||||||
import { get, post } from '@/rtk/helpers/apiService'
|
import { get, post, put } from '@/rtk/helpers/apiService'
|
||||||
import { notifications } from '@mantine/notifications'
|
import { notifications } from '@mantine/notifications'
|
||||||
import { createTechnical, deleteTechnical, listTechnical } from '@/api/Admin'
|
import {
|
||||||
|
createTechnical,
|
||||||
|
deleteTechnical,
|
||||||
|
getWorkDay,
|
||||||
|
listTechnical,
|
||||||
|
updateWorkDay,
|
||||||
|
} from '@/api/Admin'
|
||||||
import { useForm } from '@mantine/form'
|
import { useForm } from '@mantine/form'
|
||||||
import { Xdelete } from '@/rtk/helpers/CRUD'
|
import { Xdelete } from '@/rtk/helpers/CRUD'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
import { DatePickerInput } from '@mantine/dates'
|
||||||
|
import { IconInfoCircle } from '@tabler/icons-react'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
||||||
|
|
||||||
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
function OrganizationSettings() {
|
function OrganizationSettings() {
|
||||||
const [activeTab, setActiveTab] = useState<string | null>('technical')
|
const [activeTab, setActiveTab] = useState<string | null>('work-day')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -36,17 +54,17 @@ function OrganizationSettings() {
|
||||||
<Box w="100%" display={'flex'} mt={15} ml={10}>
|
<Box w="100%" display={'flex'} mt={15} ml={10}>
|
||||||
<Tabs w="100%" value={activeTab} onChange={setActiveTab}>
|
<Tabs w="100%" value={activeTab} onChange={setActiveTab}>
|
||||||
<Tabs.List>
|
<Tabs.List>
|
||||||
|
<Tabs.Tab value="work-day">Work Day Setting</Tabs.Tab>
|
||||||
<Tabs.Tab value="technical">Technical Setting</Tabs.Tab>
|
<Tabs.Tab value="technical">Technical Setting</Tabs.Tab>
|
||||||
<Tabs.Tab value="second">Setting 2</Tabs.Tab>
|
|
||||||
<Tabs.Tab value="third">Setting 3</Tabs.Tab>
|
<Tabs.Tab value="third">Setting 3</Tabs.Tab>
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
|
|
||||||
<Tabs.Panel value="technical" pt="xs">
|
<Tabs.Panel value="work-day" pt="xs">
|
||||||
<TechnicalSettingTab />
|
<WorkDaySettingTab />
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
|
|
||||||
<Tabs.Panel value="second" pt="xs">
|
<Tabs.Panel value="technical" pt="xs">
|
||||||
Setting 2
|
<TechnicalSettingTab />
|
||||||
</Tabs.Panel>
|
</Tabs.Panel>
|
||||||
|
|
||||||
<Tabs.Panel value="third" pt="xs">
|
<Tabs.Panel value="third" pt="xs">
|
||||||
|
|
@ -100,8 +118,8 @@ const TechnicalSettingTab = () => {
|
||||||
? row?.level === 1
|
? row?.level === 1
|
||||||
? { backgroundColor: '#d9d2e9' }
|
? { backgroundColor: '#d9d2e9' }
|
||||||
: row?.level === 2
|
: row?.level === 2
|
||||||
? { backgroundColor: '#ffd966' }
|
? { backgroundColor: '#ffd966' }
|
||||||
: { backgroundColor: '#cfe2f3' }
|
: { backgroundColor: '#cfe2f3' }
|
||||||
: { backgroundColor: '' }
|
: { backgroundColor: '' }
|
||||||
}
|
}
|
||||||
fw={500}
|
fw={500}
|
||||||
|
|
@ -330,4 +348,126 @@ const TechnicalSettingTab = () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const WorkDaySettingTab = () => {
|
||||||
|
const [workDay, setWorkDay] = useState<any>(null)
|
||||||
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [isSaving, setIsSaving] = useState(false)
|
||||||
|
const [selectedDate, setSelectedDate] = useState<Date | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getWorkDays()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const getWorkDays = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true)
|
||||||
|
const res = await get(getWorkDay, {})
|
||||||
|
if (res.status && res.data?.length > 0) {
|
||||||
|
const item = res.data[0]
|
||||||
|
setWorkDay(item)
|
||||||
|
const parsed = dayjs(item.c_code, 'DD-MM-YYYY').toDate()
|
||||||
|
setSelectedDate(parsed)
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
notifications.show({
|
||||||
|
title: 'Error',
|
||||||
|
message: error.message ?? error,
|
||||||
|
color: 'red',
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSave = async () => {
|
||||||
|
if (!selectedDate) return
|
||||||
|
try {
|
||||||
|
setIsSaving(true)
|
||||||
|
const payload = {
|
||||||
|
c_code: dayjs(selectedDate).format('DD-MM-YYYY'),
|
||||||
|
}
|
||||||
|
const res = await put(`${updateWorkDay}`, payload)
|
||||||
|
if (res.status) {
|
||||||
|
notifications.show({
|
||||||
|
title: 'Success',
|
||||||
|
message: res.message,
|
||||||
|
color: 'green',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
notifications.show({
|
||||||
|
title: 'Error',
|
||||||
|
message: error.message ?? error,
|
||||||
|
color: 'red',
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setIsSaving(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
if (!workDay) return
|
||||||
|
const parsed = dayjs(workDay.c_code, 'DD-MM-YYYY').toDate()
|
||||||
|
setSelectedDate(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<LoadingOverlay visible={isLoading} />
|
||||||
|
|
||||||
|
<Title order={4} mb="xs">
|
||||||
|
Set up Saturday as a working day.
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Paper withBorder p="lg" radius="md">
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={12}>
|
||||||
|
<DatePickerInput
|
||||||
|
label="Saturday Work Start Date"
|
||||||
|
description="Weeks starting from this date will include Saturday as a working day."
|
||||||
|
placeholder="Select Date"
|
||||||
|
value={selectedDate}
|
||||||
|
onChange={setSelectedDate}
|
||||||
|
valueFormat="DD/MM/YYYY"
|
||||||
|
clearable={false}
|
||||||
|
excludeDate={(date) => date.getDay() === 0}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{selectedDate && (
|
||||||
|
<Alert
|
||||||
|
mt="md"
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
icon={<IconInfoCircle size={16} />}
|
||||||
|
>
|
||||||
|
Starting from{' '}
|
||||||
|
<Text span fw={500}>
|
||||||
|
{dayjs(selectedDate).format('DD/MM/YYYY')}
|
||||||
|
</Text>
|
||||||
|
, Saturdays will be treated as working days in applicable weeks.
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Group justify="flex-end" mt="lg" gap="sm">
|
||||||
|
<Button variant="default" onClick={handleReset} disabled={isSaving}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleSave} loading={isSaving}>
|
||||||
|
Save change
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
<Paper withBorder p="sm" radius="md" mt="sm" bg="gray.0">
|
||||||
|
<Text size="xs" c="dimmed">
|
||||||
|
Setting type:{' '}
|
||||||
|
<Code>{workDay?.c_type ?? 'SATURDAY_WORK_SCHEDULE'}</Code>
|
||||||
|
</Text>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default OrganizationSettings
|
export default OrganizationSettings
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue