diff --git a/Modules/Client/Http/Controllers/Api/CorporateMemberController.php b/Modules/Client/Http/Controllers/Api/CorporateMemberController.php index e711c470..05032d18 100644 --- a/Modules/Client/Http/Controllers/Api/CorporateMemberController.php +++ b/Modules/Client/Http/Controllers/Api/CorporateMemberController.php @@ -28,6 +28,9 @@ class CorporateMemberController extends Controller public function index(Request $request, $corporate_id) { switch ($request->input('type')) { + case 'employee-data': + $members = $this->corporateMemberService->getAllMemberAlarmCenter($corporate_id, $request); + return response()->json(Helper::paginateResources(DashboardMemberAlarmResources::collection($members))); case 'claim-report': $members = $this->corporateMemberService->getAllMemberClaimReports($corporate_id, $request); return response()->json(Helper::paginateResources(ClaimReportMemberResources::collection($members))); diff --git a/Modules/Client/Http/Controllers/Api/DataController.php b/Modules/Client/Http/Controllers/Api/DataController.php index f824b1e2..ae383356 100644 --- a/Modules/Client/Http/Controllers/Api/DataController.php +++ b/Modules/Client/Http/Controllers/Api/DataController.php @@ -46,12 +46,15 @@ class DataController extends Controller Member::where('person_id', $person_id)->update([ 'name' => $familyMember['name'], 'email' => $familyMember['email'], + 'relation_with_principal' => $familyMember['relation_with_principal'], + 'birth_date' => $familyMember['birth_date'], ]); Person::where('id', $person_id)->update([ 'name' => $familyMember['name'], 'email' => $familyMember['email'], 'phone' => $familyMember['phone'], + 'birth_date' => $familyMember['birth_date'] ]); } diff --git a/frontend/client-portal/src/layouts/dashboard/navbar/NavConfig.tsx b/frontend/client-portal/src/layouts/dashboard/navbar/NavConfig.tsx index f7cfa648..633d8a1b 100644 --- a/frontend/client-portal/src/layouts/dashboard/navbar/NavConfig.tsx +++ b/frontend/client-portal/src/layouts/dashboard/navbar/NavConfig.tsx @@ -26,6 +26,10 @@ const navConfig = [ { subheader: 'Case Management', items: [ + { + title: 'Employee Data', + path: '/employee-data', + }, { title: 'Alarm Center', path: '/alarm-center', diff --git a/frontend/client-portal/src/pages/EmployeeData/Index.tsx b/frontend/client-portal/src/pages/EmployeeData/Index.tsx new file mode 100644 index 00000000..64129bbc --- /dev/null +++ b/frontend/client-portal/src/pages/EmployeeData/Index.tsx @@ -0,0 +1,126 @@ +/* ---------------------------------- react --------------------------------- */ +import { useState, SyntheticEvent } from 'react'; +/* ---------------------------------- @mui ---------------------------------- */ +import { Box, Tabs, Tab, Container, Grid, Card, Stack } from '@mui/material'; +import { styled } from '@mui/material/styles'; +/* ------------------------------- components ------------------------------- */ +import Page from '../../components/Page'; +/* ---------------------------------- hooks --------------------------------- */ +import useSettings from '../../hooks/useSettings'; +import List from './List'; +import ServiceMonitoring from './ServiceMonitoring'; +import UserProfile from './UserProfile'; + +import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs'; + +/* ------------------------------ tabs setting ------------------------------ */ + +/* ---------------------------------- types --------------------------------- */ + +interface TabPanelProps { + children?: React.ReactNode; + index: number; + value: number; +} + +interface StyledTabsProps { + children?: React.ReactNode; + value: number; + onChange: (event: React.SyntheticEvent, newValue: number) => void; +} + +interface StyledTabProps { + label: string; + icon?: string | React.ReactElement; +} + +/* -------------------------------- tab style ------------------------------- */ + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +function a11yProps(index: number) { + return { + id: `simple-tab-${index}`, + 'aria-controls': `simple-tabpanel-${index}`, + }; +} + +const StyledTabs = styled((props: StyledTabsProps) => )({ + backgroundColor: '#F4F6F8', + padding: '0 24px', + '& .MuiTabs-indicator': { + display: 'flex', + justifyContent: 'space-between', + backgroundColor: 'transparent', + }, + '& .MuiTabs-indicatorSpan': { + maxWidth: 40, + backgroundColor: '#635ee7', + }, +}); + +const StyledTab = styled((props: StyledTabProps) => )( + ({ theme }) => ({ + textTransform: 'none', + fontWeight: 600, + color: theme.palette.grey[600], + marginRight: '5rem', + '&.Mui-selected': { + color: '#212B36', + borderBottom: '2px solid ' + theme.palette.primary.main, + }, + '&:hover': { + color: '#212B36', + opacity: 1, + borderBottom: '2px solid ' + theme.palette.primary.main, + }, + }) +); + +/* -------------------------------------------------------------------------- */ + +export default function Drugs() { + const { themeStretch } = useSettings(); + + const [value, setValue] = useState(0); + const handleChange = (event: SyntheticEvent, newValue: number) => { + setValue(newValue); + }; + + return ( + + + + + + + + + + + + + + + ); +} diff --git a/frontend/client-portal/src/pages/EmployeeData/List.tsx b/frontend/client-portal/src/pages/EmployeeData/List.tsx new file mode 100644 index 00000000..b6b8a19b --- /dev/null +++ b/frontend/client-portal/src/pages/EmployeeData/List.tsx @@ -0,0 +1,402 @@ +/* ---------------------------------- @mui ---------------------------------- */ +import { + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Stack, + Button, + TableSortLabel, + Box, + MenuItem +} from '@mui/material'; +import { visuallyHidden } from '@mui/utils'; +/* ---------------------------------- axios --------------------------------- */ +// import axios from 'axios'; +import axios from '../../utils/axios'; +/* ---------------------------------- react --------------------------------- */ +import { useContext, useEffect, useState } from 'react'; + +/* -------------------------------- component ------------------------------- */ +import Iconify from '../../components/Iconify'; +import BaseTablePagination from '../../components/BaseTablePagination'; +import TableComponent from '../../components/Table'; + +/* ---------------------------------- hooks --------------------------------- */ +import useMap from '../../hooks/useMap'; +/* ---------------------------------- theme --------------------------------- */ +import palette from '../../theme/palette'; +import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; +import { HeadCell, Order, PaginationTableProps } from '../../@types/table'; +import { useSearchParams, useNavigate, Link } from 'react-router-dom'; +import { fDate } from '../../utils/formatTime'; +import { format } from 'date-fns'; +import Typography from '@mui/material/Typography'; +import TableMoreMenu from '../../components/table/TableMoreMenu'; +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; + + +/* ---------------------------------- types --------------------------------- */ + +// type PaginationTableProps = { +// current_page: number; +// from: number; +// last_page: number; +// links: []; +// path: string; +// per_page: number; +// to: number; +// total: number; +// }; + +// type DataTableProps = { +// fullName: string; +// memberId: string; +// service: string; +// start_date: string; +// end_date: string; +// status: boolean | number; +// }; + +// /* -------------------------------------------------------------------------- */ + +// /* -------------------------- enchanced table head -------------------------- */ + +// type Order = 'asc' | 'desc'; + +// interface HeadCell { +// id: string; +// label: string; +// } + +// const headCells: readonly HeadCell[] = [ +// { +// id: 'name', +// label: 'Name', +// }, +// { +// id: 'member_id', +// label: 'Member ID', +// }, +// { +// id: 'service', +// label: 'Service', +// }, +// { +// id: 'start_date', +// label: 'Start Date', +// }, +// { +// id: 'end_date', +// label: 'End Date', +// }, +// { +// id: 'status', +// label: 'Status', +// }, +// ]; + +// interface EnhancedTableProps { +// onRequestSort: (event: React.MouseEvent, property: string) => void; +// order: Order; +// orderBy: string; +// } + +// function EnhancedTableHead(props: EnhancedTableProps) { +// const { order, orderBy, onRequestSort } = props; +// const createSortHandler = (property: string) => (event: React.MouseEvent) => { +// onRequestSort(event, property); +// }; + +// return ( +// +// +// No +// {headCells.map((headCell) => ( +// +// +// {headCell.label} +// {orderBy === headCell.id ? ( +// +// {order === 'desc' ? 'sorted descending' : 'sorted ascending'} +// +// ) : null} +// +// +// ))} +// +// +// ); +// } + +/* -------------------------------------------------------------------------- */ + +export default function List() { + const navigate = useNavigate(); + + const { corporateValue } = useContext(UserCurrentCorporateContext); + + const [data, setData] = useState([]); + + /* -------------------------------------------------------------------------- */ + /* setting up for the table */ + /* -------------------------------------------------------------------------- */ + const [isLoading, setIsLoading] = useState(true); + + const loadings = { + isLoading: isLoading, + setIsLoading: setIsLoading, + }; + + /* ------------------------------ handle params ----------------------------- */ + const [searchParams, setSearchParams] = useSearchParams(); + const [appliedParams, setAppliedParams] = useState({}); + + const params = { + searchParams: searchParams, + setSearchParams: setSearchParams, + appliedParams: appliedParams, + setAppliedParams: setAppliedParams, + }; + /* -------------------------------------------------------------------------- */ + + /* ------------------------------ handle order ------------------------------ */ + const [order, setOrder] = useState('asc'); + const [orderBy, setOrderBy] = useState('fullName'); + + const orders = { + order: order, + setOrder: setOrder, + orderBy: orderBy, + setOrderBy: setOrderBy, + }; + /* -------------------------------------------------------------------------- */ + + /* ---------------------------- handle pagination --------------------------- */ + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + + const [paginationTable, setPaginationTable] = useState({ + current_page: 0, + from: 0, + last_page: 0, + links: [], + path: '', + per_page: 0, + to: 0, + total: 0, + }); + + const paginations = { + page: page, + setPage: setPage, + rowsPerPage: rowsPerPage, + setRowsPerPage: setRowsPerPage, + paginationTable: paginationTable, + setPaginationTable: setPaginationTable, + }; + + /* -------------------------------------------------------------------------- */ + + /* ------------------------------ handle search ----------------------------- */ + const [searchText, setSearchText] = useState(''); + + const handleSearchSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + + if (searchText === '') { + searchParams.delete('search'); + const params = Object.fromEntries([...searchParams.entries()]); + setAppliedParams(params); + } else { + const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]); + setAppliedParams(params); + } + }; + + const searchs = { + searchText: searchText, + setSearchText: setSearchText, + handleSearchSubmit: handleSearchSubmit, + }; + + /* -------------------------------- headCell -------------------------------- */ + const headCells: HeadCell[] = [ + { + id: 'memberId', + align: 'left', + label: 'Member ID', + isSort: true, + }, + { + id: 'fullName', + align: 'left', + label: 'Name', + isSort: true, + }, + + { + id: 'start_date', + align: 'left', + label: 'Start Date', + isSort: true, + }, + { + id: 'end_date', + align: 'left', + label: 'End Date', + isSort: false, + }, + { + id: 'status', + align: 'left', + label: 'Status', + isSort: true, + }, + { + id: 'view', + align: 'center', + label: '', + isSort: true, + }, + ]; + /* -------------------------------------------------------------------------- */ + + useEffect(() => { + (async () => { + setIsLoading(true); + + await new Promise((resolve) => setTimeout(resolve, 250)); + + const parameters = + Object.keys(appliedParams).length !== 0 + ? appliedParams + : Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]); + + const response = await axios.get(`${corporateValue}/members?type=employee-data`, { + params: { ...parameters }, + }); + + console.log(response.data.data); + + setData( + response.data.data.map((obj: any) => { + return { + ...obj, + // memberId: + // + // , + status: + obj.status === 1 ? ( + + ) : ( + + ), + start_date: + theme.palette.grey[300], + borderRadius: '4px', + width: '70%', + }} + > + {obj.start_date ? format(new Date(obj.start_date), "dd MMMM yyyy HH:mm:ss") : ''} + + , + end_date: + theme.palette.grey[300], + borderRadius: '4px', + width: '70%', + }} + > + {obj.end_date ? format(new Date(obj.end_date), "d MMMM yyyy HH:mm:ss") : ''} + + , + fullName: + + {obj.fullName} + + , + memberId: + + {obj.memberId} + + , + view: + + navigate ('/employee-data/user-profile/'+obj.personId)}> + + View + + + } /> + }; + }) + ); + + setPaginationTable(response.data); + setRowsPerPage(response.data.per_page); + + + + if (searchParams.get('page')) { + //@ts-ignore + const currentPage = parseInt(searchParams.get('page')) - 1; + + paginationTable.current_page = currentPage; + setPage(currentPage); + } + + setIsLoading(false); + })(); + }, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]); + + return ( + + + + ); +} diff --git a/frontend/client-portal/src/pages/EmployeeData/UserProfile.tsx b/frontend/client-portal/src/pages/EmployeeData/UserProfile.tsx new file mode 100644 index 00000000..e5afd27e --- /dev/null +++ b/frontend/client-portal/src/pages/EmployeeData/UserProfile.tsx @@ -0,0 +1,66 @@ +// mui +import { IconButton, Container, Grid, Stack, Typography } from '@mui/material'; +// components +import Page from '../../components/Page'; +import Iconify from '../../components/Iconify'; +// utils +import useSettings from '../../hooks/useSettings'; +// section +import CardPersonalInformation from '../../sections/alarm-center/user-profile/CardPersonalInformation'; +import CardFamilyInformation from '../../sections/alarm-center/user-profile/CardFamilyInformation'; +import CardPolicyNumber from '../../sections/alarm-center/user-profile/CardPolicyNumber'; +import CardBenefitSummary from '../../sections/alarm-center/user-profile/CardBenefitSummary'; +import CardClaimHistory from '../../sections/alarm-center/user-profile/CardClaimHistory'; +// react +import { useNavigate, useParams } from 'react-router-dom'; +import ButtonBack from '../../components/ButtonBack'; +import { useEffect, useState, useContext } from 'react'; +import axios from '../../utils/axios'; +import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; + +// ---------------------------------------------------------------------- + +export default function UserProfile() { + const { themeStretch } = useSettings(); + // const navigate = useNavigate(); + const [data, setData] = useState(); + + const { corporateValue } = useContext(UserCurrentCorporateContext); + const { id } = useParams(); + + useEffect(() => { + axios + .get(corporateValue + '/members/' + id) + .then((response) => { + setData(response.data); + }) + .catch((error) => { + console.error(error); + }); + }, []); + +// console.log('data', data); + + return ( + + + + {/* navigate()}> + + */} + + Profile + + + {/* Row 1 */} + + + + + + + + + + ); +} diff --git a/frontend/client-portal/src/routes/index.tsx b/frontend/client-portal/src/routes/index.tsx index 1a10761c..5f7db4af 100644 --- a/frontend/client-portal/src/routes/index.tsx +++ b/frontend/client-portal/src/routes/index.tsx @@ -73,6 +73,26 @@ export default function Router() { }, ], }, + { + path: '/employee-data', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + { + path: '/employee-data/user-profile/:id', + element: , + } + ], + }, { path: '/alarm-center', element: ( @@ -196,6 +216,10 @@ const Login = Loadable(lazy(() => import('../pages/auth/Login'))); const Dashboard = Loadable(lazy(() => import('../pages/Dashboard/Index'))); const NotFound = Loadable(lazy(() => import('../pages/Page404'))); +// Employee Data +const EmployeeData = Loadable(lazy(() => import('../pages/EmployeeData/Index'))); +const EmployeeDataUserProfile = Loadable(lazy(() => import('../pages/EmployeeData/UserProfile'))); + // Alarm Center const AlarmCenter = Loadable(lazy(() => import('../pages/AlarmCenter/Index'))); const AlarmCenterServiceMonitoring = Loadable( diff --git a/frontend/client-portal/src/sections/alarm-center/user-profile/CardFamilyInformation.tsx b/frontend/client-portal/src/sections/alarm-center/user-profile/CardFamilyInformation.tsx index 575ffc77..ad1ce189 100644 --- a/frontend/client-portal/src/sections/alarm-center/user-profile/CardFamilyInformation.tsx +++ b/frontend/client-portal/src/sections/alarm-center/user-profile/CardFamilyInformation.tsx @@ -1,8 +1,11 @@ // mui -import { Button, Card, Stack, Typography, Grid, Switch, TextField } from '@mui/material'; +import { FormHelperText, FormControl, Button, Card, Stack, Typography, Grid, Switch, TextField, IconButton, Select, MenuItem, InputLabel } from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; // components import Iconify from '../../../components/Iconify'; -import { fDate } from '../../../utils/formatTime'; +import { fDate, fPostFormat } from '../../../utils/formatTime'; import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material'; import { useContext, useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; @@ -16,10 +19,39 @@ export default function CardFamilyInformation({ data }) { const [editedFamilyData, setEditedFamilyData] = useState({}); const { id } = useParams(); + //Check Required + //State Field + const [nameField, setNameField] = useState(''); + const [relationshipField, setRelationshipField] = useState(''); + const [birthDateField, setBirthDateField] = useState(''); + const [birthDateFieldCheck, setBirthDateFieldCheck] = useState(1); + const [emailField, setEmailField] = useState(''); + const [emailFieldCheck, setEmailFieldCheck] = useState(false); + const [phoneField, setPhoneField] = useState(''); + //State Field Error + const [nameFieldError, setNameFieldError] = useState(''); + const [relationshipFieldError, setRelationshipFieldError] = useState(''); + const [birthDateFieldError, setBirthDateFieldError] = useState(''); + const [emailFieldError, setEmailFieldError] = useState(''); + const [phoneFieldError, setPhoneFieldError] = useState(''); + const handleEditData = (index) => { setEditIndex(index); setEditedFamilyData(data?.family[index] || {}); setOpenDialog(true); + + setNameField(data?.family[index].name); + setRelationshipField(data?.family[index].relation_with_principal); + setBirthDateField(data?.family[index].birth_date); + setEmailField(data?.family[index].email); + setEmailFieldCheck(isValidEmail(data?.family[index].email)); + setPhoneField(data?.family[index].phone); + + setNameFieldError(''); + setRelationshipFieldError(''); + setBirthDateFieldError(''); + setEmailFieldError(''); + setPhoneFieldError(''); }; const handleCloseDialog = () => { @@ -37,10 +69,14 @@ export default function CardFamilyInformation({ data }) { updatedFamily[editIndex] = editedFamilyData; // Perbarui data utama dengan data keluarga yang telah diperbarui - const updatedData = { ...data, family: updatedFamily }; + const updatedData = { ...data, family: updatedFamily[editIndex]}; + + updatedData.family.birth_date = fPostFormat(updatedData.family.birth_date, 'yyyy-MM-dd') ; + + const familyArray = [updatedData.family]; axios - .post('/update-family', updatedData.family) + .post('/update-family', familyArray) .then((response) => { enqueueSnackbar('Data updated successfully', { variant: 'success' }); setOpenDialog(false); @@ -55,6 +91,17 @@ export default function CardFamilyInformation({ data }) { } } }; + + console.log(data?.family); + + const isValidEmail = (email) => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + }; + + const isRequiredFieldsFilled = () => { + return nameField.trim() !== '' && relationshipField.trim() !== '' && birthDateField !== '' && birthDateFieldCheck !== 0 && emailField.trim() !== '' && emailFieldCheck && phoneField.trim() !== ''; + }; return ( @@ -66,17 +113,17 @@ export default function CardFamilyInformation({ data }) { sx={{ paddingY: 1, paddingX: 3 }} > Beneficiary / Family - + {/**/} {/* Stack 2 */} - + {data?.family.map((familyMember, index) => ( {/* Stack 1 */} user-profile + + {familyMember?.name} + + {familyMember.relation_with_principal === 'H' ? 'Husband' : familyMember.relation_with_principal === 'W' @@ -101,29 +152,72 @@ export default function CardFamilyInformation({ data }) { ? 'Daughter' : ''} + {/* Row 2 */} - + Suspend - - {familyMember?.name} - - - {familyMember?.birth_date ? fDate(familyMember?.birth_date) : ''} - - - {familyMember?.phone} - - {/* Stack 2 */} + + Date of Birth + + + {familyMember?.birth_date ? fDate(familyMember?.birth_date) : ''} + + + + + Email + + + {familyMember?.email} + + + + + Phone Number + + + {familyMember?.phone} + + + + + Status + + + { + familyMember?.phone === '1' + ? 'Active' + : 'Inactive' + } + + + {/* Stack 2 */} + @@ -136,32 +230,117 @@ export default function CardFamilyInformation({ data }) { ))} {/* Dialog */} - - Edit Data + + + + + + Edit Data + + + + + + setEditedFamilyData({ ...editedFamilyData, name: e.target.value })} + onChange={(e) => { + setEditedFamilyData({ ...editedFamilyData, name: e.target.value }) + setNameField(e.target.value); + setNameFieldError(e.target.value.trim() === '' ? 'This field is required' : ''); + }} fullWidth sx={{ marginTop: '16px' }} + error={!!nameFieldError} + helperText={nameFieldError} /> - + + + Relationship + + + {relationshipFieldError} + + + + { + setEditedFamilyData({ ...editedFamilyData, birth_date: newValue }); + setBirthDateField(newValue); + setBirthDateFieldError(newValue === '' || newValue === null ? 'This field is required' : ''); + if(newValue !== null) + { + newValue = newValue.toString(); + setBirthDateFieldCheck(newValue === 'Invalid Date' ? 0 : 1); + } + }} + inputFormat="dd-MM-yyyy" + renderInput={(params) => } + /> + + {birthDateFieldError} + setEditedFamilyData({ ...editedFamilyData, email: e.target.value })} + onChange={(e) => { + setEditedFamilyData({ ...editedFamilyData, email: e.target.value }) + setEmailField(e.target.value); + setEmailFieldError( + e.target.value.trim() === '' ? 'This field is required' : !isValidEmail(e.target.value) ? 'Invalid email address' : '' + ); + setEmailFieldCheck(isValidEmail(e.target.value)); + }} fullWidth sx={{ marginTop: '16px' }} + error={!!emailFieldError} + helperText={emailFieldError} /> - setEditedFamilyData({ ...editedFamilyData, phone: e.target.value })} + onChange={(e) => { + let input = e.target.value; + // Hanya izinkan angka dan karakter '+' + input = input.replace(/[^0-9+]/g, ''); + // Batasi panjang input menjadi 15 digit + const maxLength = 15; + const sanitizedInput = input.slice(0, maxLength); + setEditedFamilyData({ ...editedFamilyData, phone: sanitizedInput }); + setPhoneField(sanitizedInput); + setPhoneFieldError(e.target.value.trim() === '' ? 'This field is required' : ''); + }} fullWidth sx={{ marginTop: '16px' }} + inputProps={{ + inputMode: 'tel', + pattern: '^\\+?[0-9]*$', // Memungkinkan karakter '+' di awal, diikuti oleh angka + }} + error={!!phoneFieldError} + helperText={phoneFieldError} /> @@ -169,7 +348,7 @@ export default function CardFamilyInformation({ data }) { - diff --git a/frontend/client-portal/src/sections/alarm-center/user-profile/CardPersonalInformation.tsx b/frontend/client-portal/src/sections/alarm-center/user-profile/CardPersonalInformation.tsx index 73a0d842..93240d28 100644 --- a/frontend/client-portal/src/sections/alarm-center/user-profile/CardPersonalInformation.tsx +++ b/frontend/client-portal/src/sections/alarm-center/user-profile/CardPersonalInformation.tsx @@ -1,5 +1,6 @@ // mui -import { Button, IconButton, Card, Stack, Typography, TextField } from '@mui/material'; +import { Button, IconButton, Card, Stack, Typography, TextField, InputLabel } from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; import { CardMembership, Visibility as VisibilityIcon } from '@mui/icons-material'; // components import Iconify from '../../../components/Iconify'; @@ -22,10 +23,42 @@ export default function CardPersonalInformation({ data }) { const [phone, setPhone] = useState(data?.phone || ''); const [address, setAddress] = useState(data?.main_address_id || ''); - /* const [updatedData, setUpdatedData] = useState(data); */ + //Check Required + const [nameField, setNameField] = useState(''); + const [weightField, setWeightField] = useState(''); + const [heightField, setHeightField] = useState(''); + const [emailField, setEmailField] = useState(''); + const [emailFieldCheck, setEmailFieldCheck] = useState(false); + const [phoneField, setPhoneField] = useState(''); + + const [nameFieldError, setNameFieldError] = useState(''); + const [weightFieldError, setWeightFieldError] = useState(''); + const [heightFieldError, setHeightFieldError] = useState(''); + const [emailFieldError, setEmailFieldError] = useState(''); + const [phoneFieldError, setPhoneFieldError] = useState(''); + + useEffect(() => { + // Periksa apakah data sudah terdefinisi + if (data) { + // Atur state sesuai dengan data yang diterima + setNameField(data?.name || ''); + setWeightField((data?.last_weight_kg || '').toString()); + setHeightField((data?.last_height_cm || '').toString()); + setEmailField(data?.email || ''); + setEmailFieldCheck(isValidEmail(data?.email)); + setPhoneField(data?.phone || ''); + } + }, [data]); + + const isValidEmail = (email) => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + }; + + const isRequiredFieldsFilled = () => { + return nameField.trim() !== '' && weightField.trim() !== '' && heightField.trim() !== '' && emailField.trim() !== '' && emailFieldCheck && phoneField.trim() !== ''; + }; - - //console.log(data); const handleEditData = () => { setWeight(data?.last_weight_kg || ''); @@ -35,6 +68,20 @@ export default function CardPersonalInformation({ data }) { setAddress(data?.main_address_id || ''); setEditedData(data); setOpenDialog(true); + if (data) { + // Atur state sesuai dengan data yang diterima + setNameField(data?.name || ''); + setWeightField((data?.last_weight_kg || '').toString()); + setHeightField((data?.last_height_cm || '').toString()); + setEmailField(data?.email || ''); + setPhoneField(data?.phone || ''); + + setNameFieldError(''); + setWeightFieldError(''); + setHeightFieldError(''); + setEmailFieldError(''); + setPhoneFieldError(''); + } }; const handleCloseDialog = () => { @@ -79,10 +126,10 @@ export default function CardPersonalInformation({ data }) { justifyContent="space-between" sx={{ paddingY: 1, paddingX: 3 }} > - Informasi Pribadi - + */} {/* Stack 2 */} @@ -110,100 +157,100 @@ export default function CardPersonalInformation({ data }) { - - Nama Lengkap + + Full Name {data?.name} - - Berat Badan + + Weight {data?.last_weight_kg} kg - - Tinggi Badan + + Height {data?.last_height_cm} cm {/* Stack 3 */} - + {/* Stack 3.1 */} - Informasi Dasar + {/*Informasi Dasar*/} - - Tempat Lahir + + Place of Birth {data?.birth_place} - - Tanggal Lahir + + Date of Birth {' '} {data?.birth_date ? fDate(data?.birth_date) : ''} - - Jenis Kelamin + + Gender {data?.gender ? data.gender.charAt(0).toUpperCase() + data.gender.slice(1) : ''} {/* Stack 3.2 */} - Informasi Kontak + {/*Informasi Kontak*/} - - Nomor Telpon + + Phone Number {data?.phone} - + Email {data?.email} + + - Alamat + Address {data?.main_address_id} {/* Stack 3.3 */} - Identitas Diri + {/*Identitas Diri*/} - - Nomor NIK + + ID Member {data?.nik} - - + */} {/* Stack 3.4 */} - Informasi Lainnya + {/*Informasi Lainnya*/} - - Agama - {data?.religion} - - - Status + + Marital Status {data?.marital_status} - - Pendidikan + + Education {data?.last_education} - - Pekerjaan + + Occupation {data?.current_employment} @@ -211,44 +258,124 @@ export default function CardPersonalInformation({ data }) { {/* Dialog */} - - Edit Data + + + + + + Edit Data + + + + + + setEditedData({ ...editedData, name: e.target.value })} + onChange={(e) =>{ + setEditedData({ ...editedData, name: e.target.value }); + setNameField(e.target.value); + setNameFieldError(e.target.value.trim() === '' ? 'This field is required' : ''); + }} fullWidth sx={{ marginTop: '16px' }} + inputProps={{ maxLength: 50 }} + error={!!nameFieldError} + helperText={nameFieldError} /> setWeight(e.target.value)} + onChange={(e) => { + let input = e.target.value; + // Hanya izinkan angka + input = input.replace(/[^0-9]/g, ''); + // Batasi panjang input menjadi 3 digit + const maxLength = 3; + const sanitizedInput = input.slice(0, maxLength); + setWeight(sanitizedInput); + setWeightField(sanitizedInput); + setWeightFieldError(e.target.value.trim() === '' ? 'This field is required' : ''); + }} fullWidth sx={{ marginTop: '16px' }} + inputProps={{ + inputMode: 'numeric', + pattern: '[0-9]*', // Memungkinkan hanya karakter angka + }} + error={!!weightFieldError} + helperText={weightFieldError} /> setHeight(e.target.value)} + onChange={(e) => { + let input = e.target.value; + // Hanya izinkan angka + input = input.replace(/[^0-9]/g, ''); + // Batasi panjang input menjadi 3 digit + const maxLength = 3; + const sanitizedInput = input.slice(0, maxLength); + setHeight(sanitizedInput); + setHeightField(sanitizedInput); + setHeightFieldError(e.target.value.trim() === '' ? 'This field is required' : ''); + }} fullWidth sx={{ marginTop: '16px' }} + inputProps={{ + inputMode: 'numeric', + pattern: '[0-9]*', // Memungkinkan hanya karakter angka + }} + error={!!heightFieldError} + helperText={heightFieldError} + /> setEmail(e.target.value)} + onChange={(e) => { + setEmail(e.target.value) + setEmailField(e.target.value); + setEmailFieldError( + e.target.value.trim() === '' ? 'This field is required' : !isValidEmail(e.target.value) ? 'Invalid email address' : '' + ); + setEmailFieldCheck(isValidEmail(e.target.value)); + }} fullWidth sx={{ marginTop: '16px' }} + error={!!emailFieldError} + helperText={emailFieldError} /> setPhone(e.target.value)} + onChange={(e) => { + let input = e.target.value; + // Hanya izinkan angka dan karakter '+' + input = input.replace(/[^0-9+]/g, ''); + // Batasi panjang input menjadi 13 digit + const maxLength = 15; + const sanitizedInput = input.slice(0, maxLength); + setPhone(sanitizedInput); + setPhoneField(sanitizedInput); + setPhoneFieldError(e.target.value.trim() === '' ? 'This field is required' : ''); + }} fullWidth sx={{ marginTop: '16px' }} + inputProps={{ + inputMode: 'tel', + pattern: '^\\+?[0-9]*$', // Memungkinkan karakter '+' di awal, diikuti oleh angka + }} + error={!!phoneFieldError} + helperText={phoneFieldError} /> {/* - diff --git a/frontend/client-portal/src/sections/dashboard/DialogClaimSubmitMember.tsx b/frontend/client-portal/src/sections/dashboard/DialogClaimSubmitMember.tsx index fe03e77f..f65a7b00 100644 --- a/frontend/client-portal/src/sections/dashboard/DialogClaimSubmitMember.tsx +++ b/frontend/client-portal/src/sections/dashboard/DialogClaimSubmitMember.tsx @@ -133,7 +133,7 @@ export default function DialogClaimSubmitMember({ const response = await axios.get(`${corporateValue}/members`, { params: { ...appliedParams, type: 'claim-submit' }, }); - + //console.log(response.data.data); setData(response.data.data); } })();