Merge pull request 'feat(setting): add setting saturday work' (#154) from vi into master

Reviewed-on: #154
This commit is contained in:
joseph 2026-04-09 19:24:31 +10:00
commit b22c9b9cc5
5 changed files with 204 additions and 10 deletions

View File

@ -3,6 +3,7 @@
namespace Modules\Admin\app\Http\Controllers;
use App\Http\Controllers\Controller;
use Carbon\Carbon;
use Illuminate\Http\Request;
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();
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'
]);
}
}

View File

@ -8,4 +8,12 @@ use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
use HasFactory;
protected $fillable = [
'c_type',
'c_name',
'c_code',
'c_value',
'c_active',
];
}

View File

@ -149,6 +149,8 @@ Route::middleware('api')
'prefix' => 'category',
], function () {
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([

View File

@ -35,6 +35,8 @@ export const updateWorkingDays =
//Category
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
export const getLeaveManagement = API_URL + 'v1/admin/leave-management'

View File

@ -1,28 +1,46 @@
import { useEffect, useState } from 'react'
import {
Alert,
Box,
Button,
Code,
Dialog,
Flex,
Grid,
Group,
Loader,
LoadingOverlay,
Modal,
Paper,
Select,
Tabs,
Text,
TextInput,
Title,
} from '@mantine/core'
import classes from './OrganizationSettings.module.css'
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 { createTechnical, deleteTechnical, listTechnical } from '@/api/Admin'
import {
createTechnical,
deleteTechnical,
getWorkDay,
listTechnical,
updateWorkDay,
} from '@/api/Admin'
import { useForm } from '@mantine/form'
import { Xdelete } from '@/rtk/helpers/CRUD'
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() {
const [activeTab, setActiveTab] = useState<string | null>('technical')
const [activeTab, setActiveTab] = useState<string | null>('work-day')
return (
<div>
@ -36,17 +54,17 @@ function OrganizationSettings() {
<Box w="100%" display={'flex'} mt={15} ml={10}>
<Tabs w="100%" value={activeTab} onChange={setActiveTab}>
<Tabs.List>
<Tabs.Tab value="work-day">Work Day 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.List>
<Tabs.Panel value="technical" pt="xs">
<TechnicalSettingTab />
<Tabs.Panel value="work-day" pt="xs">
<WorkDaySettingTab />
</Tabs.Panel>
<Tabs.Panel value="second" pt="xs">
Setting 2
<Tabs.Panel value="technical" pt="xs">
<TechnicalSettingTab />
</Tabs.Panel>
<Tabs.Panel value="third" pt="xs">
@ -100,8 +118,8 @@ const TechnicalSettingTab = () => {
? row?.level === 1
? { backgroundColor: '#d9d2e9' }
: row?.level === 2
? { backgroundColor: '#ffd966' }
: { backgroundColor: '#cfe2f3' }
? { backgroundColor: '#ffd966' }
: { backgroundColor: '#cfe2f3' }
: { backgroundColor: '' }
}
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