From 86076ff1d74c95357bd1da9644aca61c9deba79e Mon Sep 17 00:00:00 2001 From: Muhammad Fajar Date: Mon, 5 Dec 2022 08:54:24 +0700 Subject: [PATCH 01/11] fix notification login --- frontend/client-portal/src/pages/Dashboard/Dashboard.tsx | 2 -- .../client-portal/src/sections/auth/login/LoginEmailForm.tsx | 5 +++++ .../client-portal/src/sections/auth/login/LoginPhoneForm.tsx | 5 +++++ .../client-portal/src/sections/auth/login/VerifyCodeForm.tsx | 5 +++-- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx index d9da4786..17937595 100755 --- a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx +++ b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx @@ -8,7 +8,6 @@ import Page from '../../components/Page'; import CardNotification from '../../sections/dashboard/CardNotification'; import CardBalance from '../../sections/dashboard/CardBalance'; import TableList from '../../sections/dashboard/TableList'; -import List from './List'; // ---------------------------------------------------------------------- @@ -57,7 +56,6 @@ export default function Dashboard() { - {/* */} diff --git a/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx b/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx index 1a3ba1b3..e393c4bd 100755 --- a/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx +++ b/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx @@ -11,6 +11,7 @@ import useAuth from '../../../hooks/useAuth'; import useIsMountedRef from '../../../hooks/useIsMountedRef'; /* ------------------------------- components ------------------------------- */ import { FormProvider, RHFTextField } from '../../../components/hook-form'; +import { enqueueSnackbar } from 'notistack'; /* ---------------------------------- types --------------------------------- */ @@ -56,6 +57,10 @@ export default function LoginForm({ setEmailOrPhone, setLoginOrVerifyCode }: Log setEmailOrPhone(data.email); setLoginOrVerifyCode(true); reset(); + enqueueSnackbar('Kode OTP telah dikirim, silahkan cek email yang login', { + variant: 'success', + autoHideDuration: 2000, + }); } catch (error: any) { reset(); diff --git a/frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx b/frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx index 2309a6cd..40f4f716 100755 --- a/frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx +++ b/frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx @@ -11,6 +11,7 @@ import { FormProvider, RHFTextField } from '../../../components/hook-form'; /* ---------------------------------- hooks --------------------------------- */ import useAuth from '../../../hooks/useAuth'; import useIsMountedRef from '../../../hooks/useIsMountedRef'; +import { enqueueSnackbar } from 'notistack'; /* ---------------------------------- types --------------------------------- */ @@ -56,6 +57,10 @@ export default function LoginPhoneForm({ setEmailOrPhone, setLoginOrVerifyCode } setEmailOrPhone(0 + data.phone); setLoginOrVerifyCode(true); reset(); + enqueueSnackbar('Kode OTP telah dikirim, silahkan cek pada nomor yang telah login', { + variant: 'success', + autoHideDuration: 2000, + }); } catch (error: any) { reset(); diff --git a/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx b/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx index 47d99760..00b2737e 100755 --- a/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx +++ b/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx @@ -88,7 +88,8 @@ export default function VerifyCodeForm({ const onSubmit = async (data: FormValuesProps) => { try { - await new Promise((resolve) => setTimeout(resolve, 1000)); + enqueueSnackbar('Verify success!', { variant: 'success', autoHideDuration: 1000 }); + await new Promise((resolve) => setTimeout(resolve, 2000)); // @ts-ignore const response: responseProps = await validateOtp(emailOrPhone, Object.values(data).join('')); @@ -101,7 +102,7 @@ export default function VerifyCodeForm({ } navigate('/dashboard'); - enqueueSnackbar('Verify success!', { variant: 'success' }); + enqueueSnackbar('Login Berhasil!', { variant: 'success' }); } catch (error) { console.error(error); } From f18e7126d36a6fde6240eb3d1943d84adb82c957 Mon Sep 17 00:00:00 2001 From: Muhammad Fajar Date: Mon, 5 Dec 2022 10:23:24 +0700 Subject: [PATCH 02/11] fix dashboard --- .../src/pages/Dashboard/Dashboard.tsx | 21 +- .../src/pages/Dashboard/List.tsx | 373 ------------------ .../src/sections/dashboard/TableList.tsx | 106 ++++- 3 files changed, 97 insertions(+), 403 deletions(-) delete mode 100644 frontend/client-portal/src/pages/Dashboard/List.tsx diff --git a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx index 17937595..abbfdfe7 100755 --- a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx +++ b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx @@ -23,23 +23,6 @@ const itemList = [ export default function Dashboard() { const { themeStretch } = useSettings(); - // const [corporate, setCorporate] = useState({}); - - // const loadSomething = () => { - // axios - // .get('dashboard') - // .then((res) => { - // setCorporate(res.data.corporate); - // }) - // .catch((err) => { - // alert('Opps, Something Went Wrong when collecting dashboard data'); - // }); - // }; - - // useEffect(() => { - // loadSomething(); - // }, []); - return ( @@ -48,10 +31,10 @@ export default function Dashboard() { - + - + diff --git a/frontend/client-portal/src/pages/Dashboard/List.tsx b/frontend/client-portal/src/pages/Dashboard/List.tsx deleted file mode 100644 index dabb387b..00000000 --- a/frontend/client-portal/src/pages/Dashboard/List.tsx +++ /dev/null @@ -1,373 +0,0 @@ -// @mui -import { - Box, - Button, - Card, - Collapse, - IconButton, - InputLabel, - MenuItem, - OutlinedInput, - Paper, - Select, - SelectChangeEvent, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - TextField, - Typography, - Badge, - Tab, - Tabs, - CardHeader, - Stack, - Menu, - ButtonGroup, - Pagination, - Grid, - Autocomplete, -} from '@mui/material'; -import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; -import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; -import AddIcon from '@mui/icons-material/Add'; -import UploadIcon from '@mui/icons-material/Upload'; -import CancelIcon from '@mui/icons-material/Cancel'; -// hooks -import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; -import useSettings from '../../hooks/useSettings'; -import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; -// components -import axios from '../../utils/axios'; -import { LaravelPaginatedData } from '../../@types/paginated-data'; -import { Icd } from '../../@types/diagnosis'; -import BasePagination from '../../components/BasePagination'; -import { Member } from '../../@types/member'; -import Iconify from '../../components/Iconify'; - -const options = [ - { label: 'The Shawshank Redemption', value: 1994 }, - { label: 'The Godfather', year: 1972 }, - { label: 'The Godfather: Part II', year: 1974 }, - { label: 'The Dark Knight', year: 2008 }, - { label: '12 Angry Men', year: 1957 }, - { label: "Schindler's List", year: 1993 }, - { label: 'Pulp Fiction', year: 1994 }, -]; - -export default function List() { - const navigate = useNavigate(); - const { themeStretch } = useSettings(); - const { corporate_id } = useParams(); - const [searchParams, setSearchParams] = useSearchParams(); - const [importResult, setImportResult] = useState(null); - - function SearchInput(props: any) { - // SEARCH - const searchInput = useRef(null); - const [searchText, setSearchText] = useState(''); - - const [value, setValue] = useState(options[0]); - const [inputValue, setInputValue] = useState(''); - - const handleSearchChange = (event: any) => { - const newSearchText = event.target.value ?? ''; - setSearchText(newSearchText); - }; - - const handleSearchSubmit = (event: any) => { - event.preventDefault(); - props.onSearch(searchText); // Trigger to Parent - }; - - useEffect(() => { - // Trigger First Search - setSearchText(searchParams.get('search') ?? ''); - console.log(value, inputValue); - }, [searchParams, value, inputValue]); - - return ( -
- - { - setValue(newValue); - }} - inputValue={inputValue} - onInputChange={(event, newInputValue) => { - setInputValue(newInputValue); - }} - id="controllable-states-demo" - options={options} - sx={{ width: 300 }} - renderInput={(params) => } - /> - - -
- ); - } - - function ImportForm(props: any) { - // IMPORT - // Create Button Menu - const [anchorEl, setAnchorEl] = React.useState(null); - const createMenu = Boolean(anchorEl); - const importForm = useRef(null); - const [currentImportFileName, setCurrentImportFileName] = useState(null); - - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - const handleImportButton = () => { - if (importForm?.current) { - handleClose(); - importForm.current ? importForm.current.click() : console.log('No File selected'); - } else { - alert('No file selected'); - } - }; - - const handleCancelImportButton = () => { - importForm.current.value = ''; - importForm.current.dispatchEvent(new Event('change', { bubbles: true })); - }; - - const handleImportChange = (event: any) => { - if (event.target.files[0]) { - setCurrentImportFileName(event.target.files[0].name); - } else { - setCurrentImportFileName(null); - } - }; - - const handleUpload = () => { - if (importForm.current?.files.length) { - const formData = new FormData(); - formData.append('file', importForm.current?.files[0]); - axios - .post(`master/formularium/import`, formData) - .then((response) => { - handleCancelImportButton(); - loadDataTableData(); - setImportResult(response.data); - // alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows'); - }) - .catch((response) => { - alert( - 'Looks like something went wrong. Please check your data and try again. ' + - response.message - ); - }); - } else { - alert('No File Selected'); - } - }; - - return ( - - - - - - ); - } - - // Called on every row to map the data to the columns - function createData(member: Member): Member { - return { - ...member, - }; - } - - // Generate the every row of the table - function Row(props: { row: ReturnType }) { - const { row } = props; - const [open, setOpen] = React.useState(true); - - return ( - - *': { borderBottom: 'unset' } }}> - - setOpen(!open)}> - {open ? : } - - - {row.member_id} - {row.payor_id} - {row.name} - {row.nik} - {row.nric} - - - - - {/* */} - - {/* COLLAPSIBLE ROW */} - - - - - - - - - - - - - ); - } - - // Dummy Default Data - const [dataTableIsLoading, setDataTableLoading] = useState(true); - const [dataTableLastRequest, setDataTableLastRequest] = useState(0); - const [dataTableResponseState, setDataTableResponseState] = useState('idle'); - const [dataTableData, setDataTableData] = useState({ - current_page: 1, - data: [], - path: '', - first_page_url: '', - last_page: 1, - last_page_url: '', - next_page_url: '', - prev_page_url: '', - per_page: 10, - from: 0, - to: 0, - total: 0, - }); - const [dataTablePage, setDataTablePage] = useState(5); - - const loadDataTableData = async (appliedFilter: any | null = null) => { - setDataTableLoading(true); - const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); - const response = await axios.get('/members', { params: filter }); - - setDataTableData(response.data.members); - setDataTableLoading(false); - }; - - const headStyle = { - fontWeight: 'bold', - }; - - const applyFilter = async (searchFilter: string) => { - await loadDataTableData({ search: searchFilter }); - setSearchParams({ search: searchFilter }); - }; - - const handlePageChange = (event: ChangeEvent, value: number) => { - const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); - loadDataTableData(filter); - setSearchParams(filter); - }; - - useEffect(() => { - loadDataTableData(); - }, []); - - return ( - - - - - {/* The Main Table */} - - - - - - Member ID - - - Name - - - Divisi - - - Limit - - - Status - - - {' '} - - - - {dataTableIsLoading ? ( - - - - Loading - - - - ) : dataTableData.data.length === 0 ? ( - - - - No Data - - - - ) : ( - - {dataTableData.data.map((row) => ( - - ))} - - )} -
-
- - -
-
- ); -} diff --git a/frontend/client-portal/src/sections/dashboard/TableList.tsx b/frontend/client-portal/src/sections/dashboard/TableList.tsx index c1100464..6364a6b1 100755 --- a/frontend/client-portal/src/sections/dashboard/TableList.tsx +++ b/frontend/client-portal/src/sections/dashboard/TableList.tsx @@ -14,12 +14,14 @@ import { IconButton, Card, Grid, + Autocomplete, } from '@mui/material'; import { visuallyHidden } from '@mui/utils'; +import { Add as AddIcon } from '@mui/icons-material'; /* ---------------------------------- axios --------------------------------- */ import axios from 'axios'; /* ---------------------------------- react --------------------------------- */ -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; /* -------------------------------- component ------------------------------- */ import Iconify from '../../components/Iconify'; import BaseTablePagination from '../../components/BaseTablePagination'; @@ -177,6 +179,14 @@ export default function TableList() { }; /* -------------------------------------------------------------------------- */ + /* ----------------------------- Field Container ---------------------------- */ + + /* ----------------------------- division field ----------------------------- */ + const optionDivisions = ['All']; + + const [value, setValue] = useState(optionDivisions[0]); + /* -------------------------------------------------------------------------- */ + /* ------------------------------ Search field ------------------------------ */ const [searchText, setSearchText] = useState(''); @@ -194,6 +204,89 @@ export default function TableList() { }; /* -------------------------------------------------------------------------- */ + /* ------------------------------ import button ----------------------------- */ + const [currentImportFileName, setCurrentImportFileName] = useState(null); + const importForm = useRef(null); + + const handleImportChange = (event: any) => { + if (event.target.files[0]) { + setCurrentImportFileName(event.target.files[0].name); + } else { + setCurrentImportFileName(null); + } + }; + /* -------------------------------------------------------------------------- */ + + /* ------------------------------ create button ----------------------------- */ + const [anchorEl, setAnchorEl] = useState(null); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + /* -------------------------------------------------------------------------- */ + + const FieldContainer = () => ( + + + { + console.log(newValue); + setValue(newValue); + }} + renderInput={(params) => } + /> + + +
+ + +
+ + + + + + +
+ ); + + /* -------------------------------------------------------------------------- */ + /* ---------------------------- table pagination ---------------------------- */ /* ------------------------ button change pagination ------------------------ */ @@ -261,16 +354,7 @@ export default function TableList() { {/* Field 1 */} -
- - +
{/* End Field 1 */} {/* Field 2 */} From b26ccdd0e6ad730a5baa4b57c047894745dbcb04 Mon Sep 17 00:00:00 2001 From: Muhammad Fajar Date: Tue, 6 Dec 2022 18:48:17 +0700 Subject: [PATCH 03/11] fix division when member import --- .../Transformers/DashboardResources.php | 37 +++ .../Controllers/Api/CorporateController.php | 64 +++-- .../Api/CorporateMemberController.php | 61 +++-- .../Controllers/Api/DivisionController.php | 11 +- .../Services/MemberEnrollmentService.php | 222 ++++++++++-------- app/Models/Member.php | 63 +++-- .../2022_11_22_135948_create_claims_table.php | 7 +- 7 files changed, 261 insertions(+), 204 deletions(-) create mode 100644 Modules/Client/Transformers/DashboardResources.php diff --git a/Modules/Client/Transformers/DashboardResources.php b/Modules/Client/Transformers/DashboardResources.php new file mode 100644 index 00000000..37b7dc2e --- /dev/null +++ b/Modules/Client/Transformers/DashboardResources.php @@ -0,0 +1,37 @@ +currentPolicy->limit_balance; + $myLimitTotal = (int)$this->currentPolicy->total_premi; + + $lockBalance = (int)$this->currentPolicy->minimal_stop_service_net; + $lockPercentage = (int)$this->currentPolicy->minimal_stop_service_percentage; + + return [ + 'policy' => [ + 'myLimit' => [ + 'balance' => $myLimitBalance, + 'total' => $myLimitTotal, + 'percentage' => ($myLimitBalance / $myLimitTotal) * 100, + ], + 'lockLimit' => [ + 'balance' => $lockBalance, + 'percentage' => $lockPercentage + ] + ], + ]; + } +} diff --git a/Modules/Internal/Http/Controllers/Api/CorporateController.php b/Modules/Internal/Http/Controllers/Api/CorporateController.php index 519eb2d7..e1e0fd82 100755 --- a/Modules/Internal/Http/Controllers/Api/CorporateController.php +++ b/Modules/Internal/Http/Controllers/Api/CorporateController.php @@ -26,18 +26,17 @@ class CorporateController extends Controller public function index(Request $request) { $corporates = Corporate::query() - ->when($request->search, function ($query, $search) { - return $query->where('name', 'LIKE', '%'.$search.'%') - ->orWhere('code', 'LIKE', '%'.$search.'%'); - }) - ->with('currentPolicy', 'subCorporates') - ->withCount([ - 'employees', - 'corporatePlans', - 'corporateBenefits', - ]) - ->where('type', 'corporate') - ->paginate(10); + ->when($request->search, function ($query, $search) { + return $query->where('name', 'LIKE', '%' . $search . '%') + ->orWhere('code', 'LIKE', '%' . $search . '%'); + }) + ->with('currentPolicy', 'subCorporates') + ->withCount([ + 'employees', + 'corporateBenefits', + ]) + ->where('type', 'corporate') + ->paginate(10); return $corporates; } @@ -48,10 +47,10 @@ class CorporateController extends Controller */ public function create() { - $corporateGroups = Corporate::whereNull('parent_id')->get()->map(function($corporate) { + $corporateGroups = Corporate::whereNull('parent_id')->get()->map(function ($corporate) { return [ 'value' => $corporate->id, - 'label' => $corporate->name.' ('.$corporate->code.')', + 'label' => $corporate->name . ' (' . $corporate->code . ')', ]; }); @@ -80,7 +79,7 @@ class CorporateController extends Controller 'policy_stop_service_percentage' => 'required_with:policy_code', 'policy_stop_service_net' => 'required_with:policy_code', ]); - + try { DB::beginTransaction(); $corporate_data = $request->all(); @@ -155,7 +154,7 @@ class CorporateController extends Controller 'policy_stop_service_percentage' => 'required_with:policy_code', 'policy_stop_service_net' => 'required_with:policy_code', ]); - + try { DB::beginTransaction(); @@ -199,7 +198,8 @@ class CorporateController extends Controller // } - public function activation(Request $request, $corporate_id) { + public function activation(Request $request, $corporate_id) + { $request->validate([ 'active' => 'required' ]); @@ -223,16 +223,16 @@ class CorporateController extends Controller 'file' => 'required|file|mimes:xls,xlsx,csv,txt', ]); // dd($request->toArray()); - $file_name = now()->getPreciseTimestamp(3).'-'.$request->file('file')->getClientOriginalName(); + $file_name = now()->getPreciseTimestamp(3) . '-' . $request->file('file')->getClientOriginalName(); $file = $request->file('file')->storeAs('temp', $file_name); $corporate = Corporate::findOrFail($corporate_id); - + $import = new ImportService(); - $import->read(Storage::path('temp/'.$file_name)); - $import->write(Storage::disk('public')->path('temp/result-'.$file_name), 'xsls'); + $import->read(Storage::path('temp/' . $file_name)); + $import->write(Storage::disk('public')->path('temp/result-' . $file_name), 'xsls'); foreach ($import->sheetsIterator() as $sheetIndex => $sheet) { - if ( !in_array($sheet->getName(), ['Plan', 'Benefit']) ) { + if (!in_array($sheet->getName(), ['Plan', 'Benefit'])) { continue; } else { if ($sheetIndex == 1) { // Rename First Sheet to Writer @@ -243,9 +243,9 @@ class CorporateController extends Controller $nextWriterSheet->setName($sheet->getName()); } - if ( $sheet->getName() == 'Plan' ) { + if ($sheet->getName() == 'Plan') { $headers_map_to_table_fields = Plan::$doc_headers_to_field_map; - } else if ( $sheet->getName() == 'Benefit' ) { + } else if ($sheet->getName() == 'Benefit') { $headers_map_to_table_fields = Benefit::$doc_headers_to_field_map; } @@ -253,7 +253,6 @@ class CorporateController extends Controller $result_headers = array_keys($headers_map_to_table_fields); $result_headers = array_merge($result_headers, ['Ingest Code', 'Ingest Note']); $import->addArrayToRow($result_headers); - } $doc_headers_indexes = []; @@ -261,8 +260,8 @@ class CorporateController extends Controller if ($index == 1) { // First Row Must be Header foreach ($row->getCells() as $index => $cell) { $title = $cell->getValue(); - $title = preg_replace( "/\r|\n/", " ", $title ); - $title = preg_replace('/\xc2\xa0/', " ", $title ); + $title = preg_replace("/\r|\n/", " ", $title); + $title = preg_replace('/\xc2\xa0/', " ", $title); $title = rtrim($title); $title = ltrim($title); $doc_headers_indexes[$index] = $title; @@ -278,9 +277,9 @@ class CorporateController extends Controller try { // Process the Row Data $corporateService = new CorporateService(); - if ( $sheet->getName() == 'Plan' ) { + if ($sheet->getName() == 'Plan') { $corporateService->handlePlanRow($corporate, $row_data); - } else if ( $sheet->getName() == 'Benefit' ) { + } else if ($sheet->getName() == 'Benefit') { $corporateService->handleBenefitRow($corporate, $row_data); } @@ -289,7 +288,6 @@ class CorporateController extends Controller 'Ingest Code' => 200, 'Ingest Note' => 'Success', ]), $sheet->getName()); - } catch (ImportRowException $e) { // Write Data Validation Error to File $import->addArrayToRow(array_merge($row_data, [ @@ -308,7 +306,7 @@ class CorporateController extends Controller } } $import->reader->close(); - Storage::delete('temp/'.$file_name); + Storage::delete('temp/' . $file_name); $import->writer->close(); return [ @@ -316,8 +314,8 @@ class CorporateController extends Controller // 'total_failed_row' => count($failed_plan_data), // 'failed_row' => $failed_plan_data, 'result_file' => [ - 'url' => Storage::disk('public')->url('temp/result-'.$file_name), - 'name' => 'result-'.$file_name, + 'url' => Storage::disk('public')->url('temp/result-' . $file_name), + 'name' => 'result-' . $file_name, ] ]; } diff --git a/Modules/Internal/Http/Controllers/Api/CorporateMemberController.php b/Modules/Internal/Http/Controllers/Api/CorporateMemberController.php index c44197ba..620f54e3 100755 --- a/Modules/Internal/Http/Controllers/Api/CorporateMemberController.php +++ b/Modules/Internal/Http/Controllers/Api/CorporateMemberController.php @@ -27,22 +27,22 @@ class CorporateMemberController extends Controller public function index(Request $request, $corporate_id) { $members = Member::query() - ->filter($request->all()) - // ->where('corporate_id', $corporate_id) - ->whereHas('employeds', function ($employeds) use ($corporate_id) { - $employeds->where('corporate_id', $corporate_id); - }) - ->with([ - 'employeds', - 'currentPolicy', - 'claims' => function ($claim) { - return $claim->used(); - } - ]) - ->with('currentPlan') - ->paginate() - ->appends($request->all()); - + ->filter($request->all()) + // ->where('corporate_id', $corporate_id) + ->whereHas('employeds', function ($employeds) use ($corporate_id) { + $employeds->where('corporate_id', $corporate_id); + }) + ->with([ + 'employeds', + 'currentPolicy', + // 'claims' => function ($claim) { + // return $claim->used(); + // } + ]) + ->with('currentPlan') + ->paginate() + ->appends($request->all()); + return $members; } @@ -111,17 +111,17 @@ class CorporateMemberController extends Controller { $request->validate([ 'file' => 'required|file|mimes:xls,xlsx,csv,txt', - ]); + ]); $corporate = Corporate::findOrFail($corporate_id)->load('currentPolicy'); - - $file_name = now()->getPreciseTimestamp(3).'-'.$request->file('file')->getClientOriginalName(); + + $file_name = now()->getPreciseTimestamp(3) . '-' . $request->file('file')->getClientOriginalName(); $file = $request->file('file')->storeAs('temp', $file_name); - $reader = ReaderEntityFactory::createReaderFromFile(Storage::path('temp/'.$file_name)); - $reader->open(Storage::path('temp/'.$file_name)); - + $reader = ReaderEntityFactory::createReaderFromFile(Storage::path('temp/' . $file_name)); + $reader->open(Storage::path('temp/' . $file_name)); + $writer = WriterEntityFactory::createXLSXWriter(); - $writer->openToFile(Storage::disk('public')->path('temp/result-'.$file_name)); + $writer->openToFile(Storage::disk('public')->path('temp/result-' . $file_name)); $headers_map_to_table_fields = $this->memberEnrollmentService->doc_headers_to_field_map; @@ -139,8 +139,8 @@ class CorporateMemberController extends Controller foreach ($row->getCells() as $index => $cell) { // Clear up the string and remove all spaces $title = $cell->getValue(); - $title = preg_replace( "/\r|\n/", " ", $title ); - $title = preg_replace('/\xc2\xa0/', " ", $title ); + $title = preg_replace("/\r|\n/", " ", $title); + $title = preg_replace('/\xc2\xa0/', " ", $title); $title = rtrim($title); $title = ltrim($title); $doc_headers_indexes[$index] = $title; @@ -157,7 +157,7 @@ class CorporateMemberController extends Controller try { // dd($new_member_data); $rowResponse = $this->memberEnrollmentService->handleImportRow($corporate, $new_member_data); - + // Write Success Result to File $singleRow = WriterEntityFactory::createRow($this->memberEnrollmentService->makeResultRowWithResultFormat($rowResponse)); $writer->addRow($singleRow); @@ -182,14 +182,13 @@ class CorporateMemberController extends Controller $failed_member_data[] = ['row_number' => $index, 'error' => $e->getMessage()]; } } - } - + break; //only read first sheet } $reader->close(); $writer->close(); - Storage::delete('temp/'.$file_name); + Storage::delete('temp/' . $file_name); // throw(404); return [ @@ -197,8 +196,8 @@ class CorporateMemberController extends Controller 'total_failed_row' => count($failed_member_data), 'failed_row' => $failed_member_data, 'result_file' => [ - 'url' => Storage::disk('public')->url('temp/result-'.$file_name), - 'name' => 'result-'.$file_name, + 'url' => Storage::disk('public')->url('temp/result-' . $file_name), + 'name' => 'result-' . $file_name, ] ]; } diff --git a/Modules/Internal/Http/Controllers/Api/DivisionController.php b/Modules/Internal/Http/Controllers/Api/DivisionController.php index fd8a690b..3584abff 100755 --- a/Modules/Internal/Http/Controllers/Api/DivisionController.php +++ b/Modules/Internal/Http/Controllers/Api/DivisionController.php @@ -17,11 +17,11 @@ class DivisionController extends Controller public function index(Request $request, $corporate_id) { $benefits = CorporateDivision::query() - ->filter($request->all()) - ->where('corporate_id', $corporate_id) - ->paginate(0) - ->appends($request->all()); - + ->filter($request->all()) + ->where('corporate_id', $corporate_id) + ->paginate(0) + ->appends($request->all()); + return $benefits; } @@ -44,7 +44,6 @@ class DivisionController extends Controller $request->validate([ 'code' => [ 'required', - Rule::unique('corporate_plans')->where('corporate_id', $corporate_id) ], 'name' => 'required' ]); diff --git a/Modules/Internal/Services/MemberEnrollmentService.php b/Modules/Internal/Services/MemberEnrollmentService.php index 50a7c283..b7b7e33b 100755 --- a/Modules/Internal/Services/MemberEnrollmentService.php +++ b/Modules/Internal/Services/MemberEnrollmentService.php @@ -27,7 +27,7 @@ class MemberEnrollmentService "Halodoc Member ID" => "halodoc_member_id", "Corporate ID" => "corporate_id", "NIK" => "nik", - "Division" => "division_code", + "Division" => "division_name", "Branch Code" => "branch_code", "Bank Info" => "banks_info", "Language" => "language", @@ -89,7 +89,7 @@ class MemberEnrollmentService "Internal Use" => "internal_use_6", "StartNoClaim" => "start_no_claim", "EndNoClaim" => "end_no_claim", - "Option Mode" => "option_mode", + "Option Mode" => "option_mode", "Policy Inforce" => "policy_in_force", "Renewal activation date" => "renewal_activation_date", "Renewal Activation Date" => "renewal_activation_date", @@ -106,7 +106,7 @@ class MemberEnrollmentService "halodoc_member_id" => "Halodoc Member ID", "corporate_id" => "Corporate ID", "nik" => "NIK", - "division_code" => "Division", + "division_name" => "Division", "branch_code" => "Branch Code", "banks_info" => "Bank Info", "language" => "Language", @@ -264,7 +264,7 @@ class MemberEnrollmentService if (!empty($row['principal_id'])) { throw new ImportRowException(__('enrollment.PRINCIPAL_ID_NOT_REQUIRED'), 0, null, $row); } - + if (empty($row['corporate_id'])) { throw new ImportRowException(__('enrollment.CORPORATE_ID_REQUIRED'), 0, null, $row); } @@ -306,34 +306,38 @@ class MemberEnrollmentService throw new ImportRowException(__('enrollment.INVALID_MARITAL_STATUS'), 0, null, $row); } - if (empty($row['member_effective_date']) ) { + if (empty($row['member_effective_date'])) { throw new ImportRowException(__('enrollment.MEMBER_EFFECTIVE_REQUIRED'), 0, null, $row); } // TODO EFFECTIVE DATE VALIDATION - if (empty($row['member_expiry_date']) ) { + if (empty($row['member_expiry_date'])) { throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_REQUIRED'), 0, null, $row); } // TODO EFFECTIVE DATE VALIDATION // TODO FKTP VALIDATION // TODO FKRTL VALIDATION - + if (!empty($row['marital_status']) && !in_array($row['marital_status'], ['S', 'M', 'D'])) { throw new ImportRowException(__('enrollment.INVALID_MARITAL_STATUS'), 0, null, $row); } - + if (empty($row['name'])) { throw new ImportRowException(__('enrollment.NAME_REQUIRED'), 0, null, $row); } - if (!empty($row['telephone_mobile']) - && !(substr($row['telephone_mobile'], 0, 4) == '+628' || substr($row['telephone_mobile'], 0, 3) == '628')) { + if ( + !empty($row['telephone_mobile']) + && !(substr($row['telephone_mobile'], 0, 4) == '+628' || substr($row['telephone_mobile'], 0, 3) == '628') + ) { throw new ImportRowException(__('enrollment.PHONE_INVALID'), 0, null, $row); } - - if (!empty($row['email']) - && !filter_var($row['email'], FILTER_VALIDATE_EMAIL)) { + + if ( + !empty($row['email']) + && !filter_var($row['email'], FILTER_VALIDATE_EMAIL) + ) { throw new ImportRowException(__('enrollment.EMAIL_INVALID'), 0, null, $row); } @@ -396,16 +400,16 @@ class MemberEnrollmentService switch ($row['record_mode']) { case "1": // New Member $member = Member::query() - ->where('member_id', $row['member_id']) - // ->whereHas('employeds', function ($query) use ($corporate) { - // $query->where('corporate_id', $corporate->id); - // }) - ->first(); - + ->where('member_id', $row['member_id']) + // ->whereHas('employeds', function ($query) use ($corporate) { + // $query->where('corporate_id', $corporate->id); + // }) + ->first(); + // Validate If Exist Member if ($member) { throw new ImportRowException(__('enrollment.MEMBER_UNIQUE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } else { @@ -418,7 +422,7 @@ class MemberEnrollmentService if ($memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -449,8 +453,28 @@ class MemberEnrollmentService ]); $memberPolicy->save(); - if (!empty($row['division'])) { - $division_id = CorporateDivision::where('code', $row['division_code'])->where('')->pluck('id'); + if (!empty($row['division_name'])) { + $division_id = CorporateDivision::query()->where('code', $row['division_name'])->pluck('id')->first(); + + if (empty($division_id)) { + $corporateCodeArray = explode(' ', $row['division_name']); + + if (!empty($corporateCodeArray[1])) { + $corporateCode = substr($corporateCodeArray[0], 0, 1) . substr($corporateCodeArray[1], 0, 1); + } elseif (!empty($corporateCodeArray[2])) { + $corporateCode = substr($corporateCodeArray[0], 0, 1) . substr($corporateCodeArray[1], 0, 1); + } else { + $corporateCode = substr($row['division_name'], 0, 1); + } + + $division = CorporateDivision::query()->create([ + 'corporate_id' => $corporate->id, + 'name' => $row['division_name'], + 'code' => $corporateCode, + ]); + + $division_id = $division->id; + } } $member->employeds()->create([ @@ -476,13 +500,13 @@ class MemberEnrollmentService break; case "2": // Member Information Update (Without Replacement Card) $member = Member::query() - ->where('member_id', $row['member_id']) - ->first(); - + ->where('member_id', $row['member_id']) + ->first(); + // Validate If Exist Member if (!$member) { throw new ImportRowException(__('enrollment.MEMBER_NOT_FOUND', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -496,23 +520,23 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } - + if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } - + $memberPolicy->member->fill($member_data); if (!$memberPolicy->member->isDirty()) { throw new ImportRowException(__('enrollment.MEMBER_NO_CHANGE'), 0, null, $row); } - + $memberPolicy->member->save(); DB::commit(); } catch (\Exception $e) { @@ -523,13 +547,13 @@ class MemberEnrollmentService break; case "3": // Member Deletion $member = Member::query() - ->where('member_id', $row['member_id']) - ->first(); - + ->where('member_id', $row['member_id']) + ->first(); + // Validate If Exist Member if (!$member) { throw new ImportRowException(__('enrollment.MEMBER_NOT_FOUND', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -541,21 +565,21 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } $member = $memberPolicy->member; $member->active = false; - + $member->save(); break; case "5": // Member Renewal Policy (without card) @@ -566,14 +590,14 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } - + if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -583,9 +607,10 @@ class MemberEnrollmentService throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_INVALID'), 0, null, $row); } - if (Carbon::parse($memberPolicy->end) > Carbon::parse(strtotime($row['member_expiry_date'])) - || $memberPolicy->end > Carbon::parse(strtotime($row['member_expiry_date'])) - ) { + if ( + Carbon::parse($memberPolicy->end) > Carbon::parse(strtotime($row['member_expiry_date'])) + || $memberPolicy->end > Carbon::parse(strtotime($row['member_expiry_date'])) + ) { throw new ImportRowException(__('enrollment.MEMBER_RENEWAL_STILL_ACTIVE'), 0, null, $row); } @@ -609,14 +634,14 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } - + if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -625,9 +650,10 @@ class MemberEnrollmentService throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_INVALID'), 0, null, $row); } - if (Carbon::parse($memberPolicy->end) > Carbon::parse(strtotime($row['member_expiry_date'])) - || $memberPolicy->end > Carbon::parse(strtotime($row['member_expiry_date'])) - ) { + if ( + Carbon::parse($memberPolicy->end) > Carbon::parse(strtotime($row['member_expiry_date'])) + || $memberPolicy->end > Carbon::parse(strtotime($row['member_expiry_date'])) + ) { throw new ImportRowException(__('enrollment.MEMBER_RENEWAL_STILL_ACTIVE'), 0, null, $row); } @@ -651,7 +677,7 @@ class MemberEnrollmentService // 'policy_id' => $row['policy_number'] // ]), 0, null, $row); // } - + // // Read Option Mode // $option_mode = explode('!', $row['option_mode']); // $corp_code = $option_mode[1] ?? null; @@ -677,7 +703,7 @@ class MemberEnrollmentService $member_id_old = $member_id[0] ?? null; $member_id_new = $member_id[1] ?? null; - + $memberPolicy = MemberPolicy::query() ->where('policy_id', $policy_number_old) ->where('member_id', $member_id_old) @@ -686,14 +712,14 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $member_id_old, + 'member_id' => $member_id_old, 'policy_id' => $policy_number_old ]), 0, null, $row); } - + if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $member_id_old, + 'member_id' => $member_id_old, 'policy_id' => $policy_number_old ]), 0, null, $row); } @@ -702,12 +728,12 @@ class MemberEnrollmentService if (!empty($row['principal_id'])) { throw new ImportRowException(__('enrollment.PRINCIPAL_ID_NOT_REQUIRED'), 0, null, $row); } - + if (empty($row['corporate_id'])) { throw new ImportRowException(__('enrollment.CORPORATE_ID_REQUIRED'), 0, null, $row); } } - + if ($record_type_new == 'D') { if (empty($row['principal_id'])) { throw new ImportRowException(__('enrollment.PRINCIPAL_ID_REQUIRED'), 0, null, $row); @@ -739,19 +765,19 @@ class MemberEnrollmentService // 'policy_id' => $row['policy_number'] // ]), 0, null, $row); // } - + try { DB::beginTransaction(); - - if ( !empty($record_type_new) ) { + + if (!empty($record_type_new)) { $member = $memberPolicy->member; $member->record_type = $record_type_new; $member->principal_id = $row['principal_id']; $member->save(); } - - if ( !empty($corp_code_new) ) { + + if (!empty($corp_code_new)) { $oldCorporate = Corporate::where('code', $corp_code_old)->first(); $newCorporate = Corporate::where('code', $corp_code_new)->first(); @@ -759,24 +785,26 @@ class MemberEnrollmentService throw new ImportRowException(__('enrollment.CORPORATE_NOT_FOUND'), 0, null, $row); } $corporateEmployee = CorporateEmployee::where('corporate_id', $oldCorporate->id) - ->where('member_id', $memberPolicy->member->id) - ->first(); - $newCorporateEmployee = CorporateEmployee::updateOrCreate([ - 'corporate_id' => $oldCorporate->id, - 'member_id' => $memberPolicy->member->id - ], - [ - 'corporate_id' => $newCorporate->id, - 'member_id' => $memberPolicy->member->id - ]); + ->where('member_id', $memberPolicy->member->id) + ->first(); + $newCorporateEmployee = CorporateEmployee::updateOrCreate( + [ + 'corporate_id' => $oldCorporate->id, + 'member_id' => $memberPolicy->member->id + ], + [ + 'corporate_id' => $newCorporate->id, + 'member_id' => $memberPolicy->member->id + ] + ); } - if ( !empty($policy_number_new) ) { + if (!empty($policy_number_new)) { $memberPolicy->policy_id = $policy_number_new; $memberPolicy->save(); } - if ( !empty($member_id_new) ) { + if (!empty($member_id_new)) { $memberPolicy->member_id = $member_id_new; $memberPolicy->save(); @@ -800,7 +828,7 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -808,7 +836,7 @@ class MemberEnrollmentService if (Carbon::parse(strtotime($row['member_effective_date'])) < now() || Carbon::parse(strtotime($row['member_expiry_date'])) < now()) { throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_MUST_BE_AFTER_TODAY'), 0, null, $row); } - + if (Carbon::parse(strtotime($row['member_effective_date'])) > Carbon::parse(strtotime($row['member_expiry_date']))) { throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_INVALID'), 0, null, $row); } @@ -828,14 +856,14 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -851,10 +879,10 @@ class MemberEnrollmentService $newMemberPolicy->save(); break; - - - // THESE MODES BELOW ARE DISABLED + + + // THESE MODES BELOW ARE DISABLED case "4": // Member Update Start and End Date throw new ImportRowException(__('MODE 4 NOT HANDLED PROPERLY, TRY TO USE MODE 2'), 0, null, $row); break; @@ -865,14 +893,14 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } - + if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -896,9 +924,9 @@ class MemberEnrollmentService case "8": // Member Information Update (With Replacement Card) throw new ImportRowException(__('MODE 8 NOT HANDLED PROPERLY, TRY TO USE MODE 2'), 0, null, $row); break; - // case "10": // No Information Available + // case "10": // No Information Available - // break; + // break; case "11": // Advance Renewal with OLD Card No. (PRINT) throw new ImportRowException(__('MODE 11 NOT HANDLED PROPERLY, TRY TO USE MODE 13'), 0, null, $row); @@ -907,9 +935,9 @@ class MemberEnrollmentService throw new ImportRowException(__('MODE 12 NOT HANDLED PROPERLY, TRY TO USE MODE 13'), 0, null, $row); break; - // case "14": // No Information Available + // case "14": // No Information Available - // break; + // break; case "15": // Lost Card / Change Card with new card number (Print) (Rarely Used) throw new ImportRowException(__('MODE 15 NOT HANDLED PROPERLY, TRY TO USE MODE 2'), 0, null, $row); @@ -918,10 +946,10 @@ class MemberEnrollmentService throw new ImportRowException(__('MODE 16 NOT HANDLED PROPERLY, TRY TO USE MODE 2'), 0, null, $row); break; $plan = CorporatePlan::query() - ->where('corporate_id', $corporate->id) - ->where('code', $row['plan_id']) - ->where('active', true) - ->first(); + ->where('corporate_id', $corporate->id) + ->where('code', $row['plan_id']) + ->where('active', true) + ->first(); if (!$plan) { throw new ImportRowException(__('enrollment.PLAN_NOT_FOUND'), 0, null, $row); } @@ -933,14 +961,14 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } - + if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -978,7 +1006,7 @@ class MemberEnrollmentService } // This returning row with format or order as it is - public function makeResultRow($row_data) + public function makeResultRow($row_data) { $cells = []; foreach ($row_data as $cellValue) { diff --git a/app/Models/Member.php b/app/Models/Member.php index 78f4a323..8d9dcd44 100755 --- a/app/Models/Member.php +++ b/app/Models/Member.php @@ -63,7 +63,7 @@ class Member extends Model 'gender_code', '' ]; - + protected $hidden = [ 'created_at', 'updated_at', @@ -86,30 +86,30 @@ class Member extends Model public function corporates() { return $this - ->belongsToMany(Corporate::class, 'corporate_employees', 'corporate_id', 'member_id') - ->withPivot([ - 'branch_code', - 'divison_id', - 'nik', - 'status', - 'start', - 'end' - ]); + ->belongsToMany(Corporate::class, 'corporate_employees', 'corporate_id', 'member_id') + ->withPivot([ + 'branch_code', + 'division_id', + 'nik', + 'status', + 'start', + 'end' + ]); } public function currentCorporate() { return $this->belongsToMany(Corporate::class, 'corporate_employees', 'corporate_id', 'member_id') - // ->withPivot([ - // 'branch_code', - // 'divison_id', - // 'nik', - // 'status', - // 'start', - // 'end' - // ]) - ->where('start', '<', now()) - ->where('end', '>', now()); + // ->withPivot([ + // 'branch_code', + // 'divison_id', + // 'nik', + // 'status', + // 'start', + // 'end' + // ]) + ->where('start', '<', now()) + ->where('end', '>', now()); } public function memberPlans() @@ -126,7 +126,7 @@ class Member extends Model { return $this->hasOneThrough(Plan::class, MemberPlan::class, 'member_id', 'id', 'id', 'plan_id')->latest(); } - + public function policies() { return $this->hasMany(MemberPolicy::class, 'member_id', 'member_id'); @@ -135,10 +135,10 @@ class Member extends Model public function currentPolicy() { return $this->hasOneThrough(CorporatePolicy::class, MemberPolicy::class, 'member_id', 'code', 'member_id', 'policy_id') - ->where('corporate_policies.start', '<', now()) - ->where('corporate_policies.end', '>', now()) - ->where('member_policies.start', '<', now()) - ->where('member_policies.end', '>', now()); + ->where('corporate_policies.start', '<', now()) + ->where('corporate_policies.end', '>', now()) + ->where('member_policies.start', '<', now()) + ->where('member_policies.end', '>', now()); // return $this->hasOne(MemberPolicy::class, 'member_id', 'member_id')->where('status', 'active')->latestOfMany(); } @@ -174,13 +174,12 @@ class Member extends Model { $query->when($filters['search'] ?? false, function ($query, $search) { return $query - ->where('member_id', 'like', "%" . $search . "%") - ->orWhere('payor_id', 'like', "%" . $search . "%") - ->orWhere('name', 'like', "%" . $search . "%") - ; - // ->orWhereHas('corporatePlan', function ($query) use ($search) { - // $query->where('code', 'like', "%" . $search . "%"); - // }); + ->where('member_id', 'like', "%" . $search . "%") + ->orWhere('payor_id', 'like', "%" . $search . "%") + ->orWhere('name', 'like', "%" . $search . "%"); + // ->orWhereHas('corporatePlan', function ($query) use ($search) { + // $query->where('code', 'like', "%" . $search . "%"); + // }); }); } } diff --git a/database/migrations/2022_11_22_135948_create_claims_table.php b/database/migrations/2022_11_22_135948_create_claims_table.php index 6416783c..6a0fb4bd 100644 --- a/database/migrations/2022_11_22_135948_create_claims_table.php +++ b/database/migrations/2022_11_22_135948_create_claims_table.php @@ -22,15 +22,12 @@ return new class extends Migration $table->string('currency'); $table->foreignId('plan_id')->index(); $table->foreignId('benefit_id')->index(); - + $table->dateTime('requested_at')->nullable(); $table->unsignedBigInteger('requested_by')->nullable()->index(); - + $table->dateTime('received_at')->nullable(); $table->unsignedBigInteger('received_by')->nullable()->index(); - - $table->dateTime('approved_at')->nullable(); - $table->unsignedBigInteger('approved_by')->nullable()->index(); $table->dateTime('approved_at')->nullable(); $table->unsignedBigInteger('approved_by')->nullable()->index(); From 723c9f1895176fb65819e82862bab6688979487e Mon Sep 17 00:00:00 2001 From: Muhammad Fajar Date: Tue, 6 Dec 2022 18:48:31 +0700 Subject: [PATCH 04/11] create api for dashboard client portal --- .../Controllers/Api/CorporateController.php | 75 +++++++++++++++ .../Controllers/Api/DashboardController.php | 16 ++-- .../Http/Controllers/Api/UserController.php | 6 +- Modules/Client/Routes/api.php | 3 + .../src/pages/Dashboard/Dashboard.tsx | 94 ++++++++++++++++++- .../src/sections/dashboard/CardBalance.tsx | 60 +++++++----- 6 files changed, 213 insertions(+), 41 deletions(-) create mode 100755 Modules/Client/Http/Controllers/Api/CorporateController.php diff --git a/Modules/Client/Http/Controllers/Api/CorporateController.php b/Modules/Client/Http/Controllers/Api/CorporateController.php new file mode 100755 index 00000000..6cde4742 --- /dev/null +++ b/Modules/Client/Http/Controllers/Api/CorporateController.php @@ -0,0 +1,75 @@ +managedCorporates()->select(['corporates.id', 'corporates.name'])->get(); + + // corporate policy, all member list, notification + + return response()->json($corporate); + } + + /** + * Store a newly created resource in storage. + * @param Request $request + * @return Renderable + */ + public function store(Request $request) + { + } + + /** + * Show the specified resource. + * @param int $id + * @return Renderable + */ + public function show($corporate_id) + { + // + } + + /** + * Show the form for editing the specified resource. + * @param int $id + * @return Renderable + */ + public function edit($id) + { + return view('client::edit'); + } + + /** + * Update the specified resource in storage. + * @param Request $request + * @param int $id + * @return Renderable + */ + public function update(Request $request, $id) + { + // + } + + /** + * Remove the specified resource from storage. + * @param int $id + * @return Renderable + */ + public function destroy($id) + { + // + } +} diff --git a/Modules/Client/Http/Controllers/Api/DashboardController.php b/Modules/Client/Http/Controllers/Api/DashboardController.php index 5067b1cd..a655aae9 100755 --- a/Modules/Client/Http/Controllers/Api/DashboardController.php +++ b/Modules/Client/Http/Controllers/Api/DashboardController.php @@ -5,6 +5,8 @@ namespace Modules\Client\Http\Controllers\Api; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\Auth; +use Modules\Client\Transformers\DashboardResources; class DashboardController extends Controller { @@ -12,18 +14,12 @@ class DashboardController extends Controller * Display a listing of the resource. * @return Renderable */ - public function index() + public function index(Request $request) { - $user = auth()->user(); + $user = Auth::user(); + $data = DashboardResources::make($user->managedCorporates()->where('active', 1)->with('currentPolicy', 'employees')->first()); - $corporate = $user->managedCorporates() - ->withCount('employees') - ->with(['policies' => function ($policy) { - $policy->limit(1)->latest(); - }]) - ->first(); - - return response()->json(compact('corporate')); + return response()->json($data); } /** diff --git a/Modules/Client/Http/Controllers/Api/UserController.php b/Modules/Client/Http/Controllers/Api/UserController.php index b7cb8812..dce0cd0a 100755 --- a/Modules/Client/Http/Controllers/Api/UserController.php +++ b/Modules/Client/Http/Controllers/Api/UserController.php @@ -5,6 +5,7 @@ namespace Modules\Client\Http\Controllers\Api; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\Auth; class UserController extends Controller { @@ -14,7 +15,10 @@ class UserController extends Controller */ public function index() { - return response()->json(auth()->user()); + $userLogin = Auth::user(); + $corporateSelected = $userLogin->managedCorporates()->select('corporates.id')->where('active', 1)->first(); + + return response()->json(['user' => $userLogin, 'corporate' => $corporateSelected]); } /** diff --git a/Modules/Client/Routes/api.php b/Modules/Client/Routes/api.php index 2ab4c9af..78d6030d 100755 --- a/Modules/Client/Routes/api.php +++ b/Modules/Client/Routes/api.php @@ -1,6 +1,7 @@ group(function () { Route::get('/user', [UserController::class, 'index']); Route::get('dashboard', [DashboardController::class, 'index']); + Route::get('corporate', [CorporateController::class, 'index']); + Route::get('corporate/{corporate_id}', [CorporateController::class, 'show']); Route::get('members', [MemberController::class, 'index']); }); }); diff --git a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx index abbfdfe7..db95ddfc 100755 --- a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx +++ b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx @@ -1,5 +1,13 @@ // @mui -import { Typography, Container, Grid } from '@mui/material'; +import { + Typography, + Container, + Grid, + FormControl, + InputLabel, + MenuItem, + Select, +} from '@mui/material'; // hooks import useSettings from '../../hooks/useSettings'; // components @@ -8,6 +16,11 @@ import Page from '../../components/Page'; import CardNotification from '../../sections/dashboard/CardNotification'; import CardBalance from '../../sections/dashboard/CardBalance'; import TableList from '../../sections/dashboard/TableList'; +import { useEffect, useState } from 'react'; +import axios from '../../utils/axios'; +import { Stack } from '@mui/system'; +import { SelectChangeEvent } from '@mui/material/Select'; +import useAuth from '../../hooks/useAuth'; // ---------------------------------------------------------------------- @@ -20,22 +33,93 @@ const itemList = [ // ---------------------------------------------------------------------- +/* ---------------------------------- types --------------------------------- */ + +type CorporateDataProps = { + id: number; + name: string; +}; + +type CardBalanceProps = { + myLimit: { + balance: number; + total: number; + percentage: number; + }; + lockLimit: { + balance: number; + percentage: number; + }; +}; + +/* -------------------------------------------------------------------------- */ + export default function Dashboard() { const { themeStretch } = useSettings(); + const { user } = useAuth(); + + // @ts-ignore + const [corporateValue, setCorporateValue] = useState(`${user.corporate.id}`); + const [corporateData, setCorporateData] = useState([]); + const [policyData, setPolicyData] = useState({ + myLimit: { + balance: 0, + total: 0, + percentage: 0, + }, + lockLimit: { + balance: 0, + percentage: 0, + }, + }); + + const handleCorporateChange = (event: SelectChangeEvent) => { + setCorporateValue(event.target.value as string); + }; + + useEffect(() => { + (async () => { + const corporates = await axios.get('corporate'); + const dashboard = await axios.get('dashboard'); + + setCorporateData(corporates.data); + setPolicyData(dashboard.data.policy); + })(); + }, [user, corporateValue]); return ( - - Dashboard - + + + Dashboard + + + + Corporate + + + - + diff --git a/frontend/client-portal/src/sections/dashboard/CardBalance.tsx b/frontend/client-portal/src/sections/dashboard/CardBalance.tsx index dd64309c..93adf299 100644 --- a/frontend/client-portal/src/sections/dashboard/CardBalance.tsx +++ b/frontend/client-portal/src/sections/dashboard/CardBalance.tsx @@ -14,23 +14,29 @@ import Iconify from '../../components/Iconify'; import { useState } from 'react'; // utils import { fCurrency } from '../../utils/formatNumber'; -// +/* -------------------------------- sections -------------------------------- */ import DialogTopUpLimit from './DialogTopUpLimit'; import DialogClaimSubmitMember from './DialogClaimSubmitMember'; -// ---------------------------------------------------------------------- +/* ---------------------------------- types --------------------------------- */ -type DataMembers = { - name: string; - memberId: string; - saldo: string; +type CardBalanceProps = { + data: { + myLimit: { + balance: number; + total: number; + percentage: number; + }; + lockLimit: { + balance: number; + percentage: number; + }; + }; }; -type NotificationProps = { - data?: { members: DataMembers[] }; -}; +/* -------------------------------------------------------------------------- */ -// ---------------------------------------------------------------------- +/* --------------------------------- styled --------------------------------- */ const RootBalanceStyle = styled(Card)(({ theme }) => ({ boxShadow: 'none', @@ -52,19 +58,15 @@ const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ }, })); -// ---------------------------------------------------------------------- +/* -------------------------------------------------------------------------- */ -const INITIAL = '500.000.000'; -const TOTAL = 375000000; -const PERCENT = 75; - -// ---------------------------------------------------------------------- - -export default function CardBalance({ data }: NotificationProps) { +export default function CardBalance(props: CardBalanceProps) { const [openDialog, setOpenDialog] = useState(false); const [dialogTitle, setDialogTitle] = useState(''); const [isDialog, setIsDialog] = useState(''); + const { myLimit, lockLimit } = props.data; + const clickHandler = (isDialog: string) => { switch (isDialog) { case 'submitClaim': @@ -91,18 +93,26 @@ export default function CardBalance({ data }: NotificationProps) { Total Limit - {fCurrency(TOTAL)} - / {INITIAL} + + {fCurrency(myLimit ? myLimit.balance : 0)} + + + / {myLimit ? myLimit.total : 0} + - {PERCENT}% + {myLimit ? myLimit.percentage : 0}% - + @@ -113,11 +123,11 @@ export default function CardBalance({ data }: NotificationProps) { sx={{ color: '#424242', marginRight: '6px' }} /> - Lock Fund ( 25% ) + Lock Fund ( {lockLimit ? lockLimit.percentage : 0}% ) - 125.000.000 / 125.000.000 + {lockLimit ? lockLimit.balance : 0} @@ -146,7 +156,7 @@ export default function CardBalance({ data }: NotificationProps) { openDialog={openDialog} setOpenDialog={setOpenDialog} title={{ name: dialogTitle }} - data={data?.members} + // data={data?.members} /> )} From 2bac2bfeb7acb50b468e0833de8746a555057eac Mon Sep 17 00:00:00 2001 From: Muhammad Fajar Date: Wed, 7 Dec 2022 02:37:18 +0700 Subject: [PATCH 05/11] fix login & fix logout --- .../Http/Controllers/Api/AuthController.php | 2 +- app/Helpers/Helper.php | 4 +-- frontend/client-portal/src/@types/auth.ts | 2 +- .../src/contexts/LaravelAuthContext.tsx | 15 ++++------ .../dashboard/header/AccountPopover.tsx | 9 ++---- .../src/pages/Dashboard/Dashboard.tsx | 2 +- .../sections/auth/login/VerifyCodeForm.tsx | 29 +++++++++---------- 7 files changed, 25 insertions(+), 38 deletions(-) diff --git a/Modules/Client/Http/Controllers/Api/AuthController.php b/Modules/Client/Http/Controllers/Api/AuthController.php index 977302d6..ee7f3445 100755 --- a/Modules/Client/Http/Controllers/Api/AuthController.php +++ b/Modules/Client/Http/Controllers/Api/AuthController.php @@ -81,7 +81,7 @@ class AuthController extends Controller ); } - return Helper::responseJson(message: 'OTP yang anda masukan salah!'); + return Helper::responseJson(status: 'error', message: 'OTP yang anda masukan salah!'); } public function logout(Request $request) diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 34de3771..78355560 100755 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -158,10 +158,10 @@ class Helper * @param string $message * @return JsonResponse */ - public static function responseJson(array|object $data = [], int $statusCode = Response::HTTP_OK, string $message = 'Data berhasil di ambil'): JsonResponse + public static function responseJson(array|object $data = [], string $status = 'success', int $statusCode = Response::HTTP_OK, string $message = 'Data berhasil di ambil'): JsonResponse { return response()->json([ - 'status' => in_array($statusCode, [200, 201, 204]) ? 'success' : 'error', + 'status' => $status, 'statusCode' => $statusCode, 'message' => $message, 'data' => $data, diff --git a/frontend/client-portal/src/@types/auth.ts b/frontend/client-portal/src/@types/auth.ts index dbc15390..a5d2236c 100755 --- a/frontend/client-portal/src/@types/auth.ts +++ b/frontend/client-portal/src/@types/auth.ts @@ -28,7 +28,7 @@ export type JWTContextType = { method: 'jwt'; login: (phoneOrEmail: string) => Promise; validateOtp: (phoneOrEmail: string, otp: string) => Promise - logout: () => Promise; + logout: () => void; }; // export type FirebaseContextType = { diff --git a/frontend/client-portal/src/contexts/LaravelAuthContext.tsx b/frontend/client-portal/src/contexts/LaravelAuthContext.tsx index 8c887190..7c09b5fa 100755 --- a/frontend/client-portal/src/contexts/LaravelAuthContext.tsx +++ b/frontend/client-portal/src/contexts/LaravelAuthContext.tsx @@ -132,16 +132,9 @@ function AuthProvider({ children }: AuthProviderProps) { axios .post('/verify-code', { phoneOrEmail: phoneOrEmail, otp }) .then((response) => { - const { user, token } = response.data.data; + const { token } = response.data.data; setSession(token); - dispatch({ - type: Types.ValidateOtp, - payload: { - user, - }, - }); - return response.data; }) .catch((error) => { @@ -149,9 +142,11 @@ function AuthProvider({ children }: AuthProviderProps) { if (error.response.status !== 422) throw error.response; }); - const logout = async () => { - await axios.post('/logout'); + const logout = () => { setSession(null); + localStorage.removeItem('loginOrVerifyCode'); + localStorage.removeItem('emailOrPhone'); + localStorage.removeItem('emailOrPhoneForm'); dispatch({ type: Types.Logout }); }; diff --git a/frontend/client-portal/src/layouts/dashboard/header/AccountPopover.tsx b/frontend/client-portal/src/layouts/dashboard/header/AccountPopover.tsx index be9d5cbe..e9a01e1c 100755 --- a/frontend/client-portal/src/layouts/dashboard/header/AccountPopover.tsx +++ b/frontend/client-portal/src/layouts/dashboard/header/AccountPopover.tsx @@ -8,6 +8,7 @@ import { IconButtonAnimate } from '../../../components/animate'; import { useNavigate } from 'react-router-dom'; import useAuth from '../../../hooks/useAuth'; import useLocalStorage from '../../../hooks/useLocalStorage'; +import { enqueueSnackbar } from 'notistack'; // ---------------------------------------------------------------------- @@ -33,10 +34,6 @@ export default function AccountPopover() { const navigate = useNavigate(); const { logout } = useAuth(); - const [emailOrPhone, setEmailOrPhone] = useLocalStorage('emailOrPhone', ''); - const [emailOrPhoneForm, setEmailOrPhoneForm] = useLocalStorage('emailOrPhoneForm', false); - const [loginOrVerifyCode, setLoginOrVerifyCode] = useLocalStorage('loginOrVerifyCode', false); - const handleOpen = (event: React.MouseEvent) => { setOpen(event.currentTarget); }; @@ -46,11 +43,9 @@ export default function AccountPopover() { }; const handleLogout = () => { - setEmailOrPhone(''); - setEmailOrPhoneForm(false); - setLoginOrVerifyCode(false); logout(); navigate('/auth/login'); + enqueueSnackbar('Berhasi Logout!', 'success'); }; return ( diff --git a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx index db95ddfc..d20e4870 100755 --- a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx +++ b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx @@ -59,7 +59,7 @@ export default function Dashboard() { const { user } = useAuth(); // @ts-ignore - const [corporateValue, setCorporateValue] = useState(`${user.corporate.id}`); + const [corporateValue, setCorporateValue] = useState(`1`); const [corporateData, setCorporateData] = useState([]); const [policyData, setPolicyData] = useState({ myLimit: { diff --git a/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx b/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx index 00b2737e..a84d76f7 100755 --- a/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx +++ b/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx @@ -87,25 +87,22 @@ export default function VerifyCodeForm({ }, [setValue]); const onSubmit = async (data: FormValuesProps) => { - try { + // @ts-ignore + const response: responseProps = await validateOtp(emailOrPhone, Object.values(data).join('')); + + if (response.data.length === 0) { + return enqueueSnackbar(response.message, { + variant: 'error', + autoHideDuration: 4000, + preventDuplicate: true, + }); + } else { enqueueSnackbar('Verify success!', { variant: 'success', autoHideDuration: 1000 }); await new Promise((resolve) => setTimeout(resolve, 2000)); - // @ts-ignore - const response: responseProps = await validateOtp(emailOrPhone, Object.values(data).join('')); - - if (response.data.length === 0) { - return enqueueSnackbar(response.message, { - variant: 'error', - autoHideDuration: 4000, - preventDuplicate: true, - }); - } - - navigate('/dashboard'); - enqueueSnackbar('Login Berhasil!', { variant: 'success' }); - } catch (error) { - console.error(error); } + + navigate('/dashboard'); + enqueueSnackbar('Login Berhasil!', { variant: 'success' }); }; const handleChangeWithNextField = ( From e13b99a34a204613b9c626b9268b65ea370e7f10 Mon Sep 17 00:00:00 2001 From: Muhammad Fajar Date: Wed, 7 Dec 2022 02:46:24 +0700 Subject: [PATCH 06/11] fix login & logout again --- frontend/client-portal/src/contexts/LaravelAuthContext.tsx | 3 --- .../src/layouts/dashboard/header/AccountPopover.tsx | 2 +- .../client-portal/src/sections/auth/login/VerifyCodeForm.tsx | 3 +++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/client-portal/src/contexts/LaravelAuthContext.tsx b/frontend/client-portal/src/contexts/LaravelAuthContext.tsx index 7c09b5fa..896cc131 100755 --- a/frontend/client-portal/src/contexts/LaravelAuthContext.tsx +++ b/frontend/client-portal/src/contexts/LaravelAuthContext.tsx @@ -144,9 +144,6 @@ function AuthProvider({ children }: AuthProviderProps) { const logout = () => { setSession(null); - localStorage.removeItem('loginOrVerifyCode'); - localStorage.removeItem('emailOrPhone'); - localStorage.removeItem('emailOrPhoneForm'); dispatch({ type: Types.Logout }); }; diff --git a/frontend/client-portal/src/layouts/dashboard/header/AccountPopover.tsx b/frontend/client-portal/src/layouts/dashboard/header/AccountPopover.tsx index e9a01e1c..64bb2e33 100755 --- a/frontend/client-portal/src/layouts/dashboard/header/AccountPopover.tsx +++ b/frontend/client-portal/src/layouts/dashboard/header/AccountPopover.tsx @@ -45,7 +45,7 @@ export default function AccountPopover() { const handleLogout = () => { logout(); navigate('/auth/login'); - enqueueSnackbar('Berhasi Logout!', 'success'); + enqueueSnackbar('Logout Berhasil!', { variant: 'success' }); }; return ( diff --git a/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx b/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx index a84d76f7..7580971d 100755 --- a/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx +++ b/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx @@ -103,6 +103,9 @@ export default function VerifyCodeForm({ navigate('/dashboard'); enqueueSnackbar('Login Berhasil!', { variant: 'success' }); + localStorage.removeItem('loginOrVerifyCode'); + localStorage.removeItem('emailOrPhone'); + localStorage.removeItem('emailOrPhoneForm'); }; const handleChangeWithNextField = ( From f20cb5b44c7119b1d1783c5f5de68d4ce1373d3a Mon Sep 17 00:00:00 2001 From: Muhammad Fajar Date: Wed, 7 Dec 2022 03:10:02 +0700 Subject: [PATCH 07/11] fix table dashboard --- .../src/sections/dashboard/TableList.tsx | 131 ++++++++---------- 1 file changed, 57 insertions(+), 74 deletions(-) diff --git a/frontend/client-portal/src/sections/dashboard/TableList.tsx b/frontend/client-portal/src/sections/dashboard/TableList.tsx index 6364a6b1..4073d089 100755 --- a/frontend/client-portal/src/sections/dashboard/TableList.tsx +++ b/frontend/client-portal/src/sections/dashboard/TableList.tsx @@ -30,7 +30,6 @@ import palette from '../../theme/palette'; import { useSearchParams } from 'react-router-dom'; /* ---------------------------------- types --------------------------------- */ - type PaginationTableProps = { current_page: number; from: number; @@ -64,11 +63,9 @@ interface EnhancedTableProps { order: Order; orderBy: string; } - /* -------------------------------------------------------------------------- */ /* -------------------------- enchanced table head -------------------------- */ - const headCells: readonly HeadCell[] = [ { id: 'member_id', @@ -141,7 +138,6 @@ function EnhancedTableHead({ order, orderBy, onRequestSort }: EnhancedTableProps ); } - /* -------------------------------------------------------------------------- */ export default function TableList() { @@ -180,7 +176,6 @@ export default function TableList() { /* -------------------------------------------------------------------------- */ /* ----------------------------- Field Container ---------------------------- */ - /* ----------------------------- division field ----------------------------- */ const optionDivisions = ['All']; @@ -190,10 +185,6 @@ export default function TableList() { /* ------------------------------ Search field ------------------------------ */ const [searchText, setSearchText] = useState(''); - const handleSearch = (event: React.ChangeEvent) => { - setSearchText(event.target.value); - }; - const handleSearchSubmit = async (event: React.FormEvent) => { event.preventDefault(); setIsLoading(true); @@ -223,72 +214,9 @@ export default function TableList() { setAnchorEl(event.currentTarget); }; /* -------------------------------------------------------------------------- */ - - const FieldContainer = () => ( - - - { - console.log(newValue); - setValue(newValue); - }} - renderInput={(params) => } - /> - - -
- - -
- - - - - - -
- ); - /* -------------------------------------------------------------------------- */ /* ---------------------------- table pagination ---------------------------- */ - /* ------------------------ button change pagination ------------------------ */ const onPageChangeHandle = async ( event: React.MouseEvent | null, @@ -318,7 +246,6 @@ export default function TableList() { setIsLoading(false); }; /* -------------------------------------------------------------------------- */ - /* -------------------------------------------------------------------------- */ useEffect(() => { @@ -354,7 +281,63 @@ export default function TableList() { {/* Field 1 */} - + + + { + setValue(newValue); + }} + renderInput={(params) => } + /> + + +
+ setSearchText(event.target.value)} + value={searchText} + fullWidth + /> + +
+ + + + + + +
{/* End Field 1 */} {/* Field 2 */} From 3c18ae28525ad01db926bce890aea5fc8f0d2087 Mon Sep 17 00:00:00 2001 From: R Date: Wed, 7 Dec 2022 11:05:21 +0700 Subject: [PATCH 08/11] Add Prefix on URL --- .../Client/Http/Controllers/Api/DashboardController.php | 8 ++++++-- Modules/Client/Routes/api.php | 9 ++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Modules/Client/Http/Controllers/Api/DashboardController.php b/Modules/Client/Http/Controllers/Api/DashboardController.php index a655aae9..5e133d47 100755 --- a/Modules/Client/Http/Controllers/Api/DashboardController.php +++ b/Modules/Client/Http/Controllers/Api/DashboardController.php @@ -14,10 +14,14 @@ class DashboardController extends Controller * Display a listing of the resource. * @return Renderable */ - public function index(Request $request) + public function index(Request $request, $corporate_id) { $user = Auth::user(); - $data = DashboardResources::make($user->managedCorporates()->where('active', 1)->with('currentPolicy', 'employees')->first()); + $currentCorporate = $user->managedCorporates() + ->with(['currentPolicy', 'employees']) + ->findOrFail($corporate_id); + + $data = DashboardResources::make($currentCorporate); return response()->json($data); } diff --git a/Modules/Client/Routes/api.php b/Modules/Client/Routes/api.php index 78d6030d..8b5843e5 100755 --- a/Modules/Client/Routes/api.php +++ b/Modules/Client/Routes/api.php @@ -27,11 +27,18 @@ Route::prefix('client')->group(function () { Route::middleware('auth:sanctum')->group(function () { Route::post('logout', [AuthController::class, 'logout'])->name('logout'); - Route::get('/user', [UserController::class, 'index']); + Route::get('user', [UserController::class, 'index']); + Route::prefix('{corporate_id}')->group(function() { + Route::get('asd', function ($corporate_id) { + return $corporate_id; + }); + + Route::get('dashboard', [DashboardController::class, 'index']); Route::get('corporate', [CorporateController::class, 'index']); Route::get('corporate/{corporate_id}', [CorporateController::class, 'show']); Route::get('members', [MemberController::class, 'index']); }); + }); }); From 8d6b6e41d7cfee8b9577ed7e84e38b9d6817de97 Mon Sep 17 00:00:00 2001 From: R Date: Wed, 7 Dec 2022 11:13:07 +0700 Subject: [PATCH 09/11] [WIP] Change Api to currentCorporate --- Modules/Client/Http/Controllers/Api/DashboardController.php | 2 +- frontend/client-portal/src/pages/Dashboard/Dashboard.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Modules/Client/Http/Controllers/Api/DashboardController.php b/Modules/Client/Http/Controllers/Api/DashboardController.php index 5e133d47..441c97af 100755 --- a/Modules/Client/Http/Controllers/Api/DashboardController.php +++ b/Modules/Client/Http/Controllers/Api/DashboardController.php @@ -19,7 +19,7 @@ class DashboardController extends Controller $user = Auth::user(); $currentCorporate = $user->managedCorporates() ->with(['currentPolicy', 'employees']) - ->findOrFail($corporate_id); + ->find($corporate_id); $data = DashboardResources::make($currentCorporate); diff --git a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx index d20e4870..13abba5d 100755 --- a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx +++ b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx @@ -57,6 +57,7 @@ type CardBalanceProps = { export default function Dashboard() { const { themeStretch } = useSettings(); const { user } = useAuth(); + const [currentCorporate, setCurrentCorporate] = useState({id: 1}); // @ts-ignore const [corporateValue, setCorporateValue] = useState(`1`); @@ -79,8 +80,8 @@ export default function Dashboard() { useEffect(() => { (async () => { - const corporates = await axios.get('corporate'); - const dashboard = await axios.get('dashboard'); + const corporates = await axios.get(`${currentCorporate.id}/dashboard`); + const dashboard = await axios.get(`${currentCorporate.id}/dashboard`); setCorporateData(corporates.data); setPolicyData(dashboard.data.policy); From 5840a311e0670654cd11271c8dcfaee80a993e41 Mon Sep 17 00:00:00 2001 From: Muhammad Fajar Date: Wed, 7 Dec 2022 11:15:33 +0700 Subject: [PATCH 10/11] fix dashboard table --- .../Controllers/Api/DashboardController.php | 2 +- .../Controllers/Api/DivisionController.php | 120 ++++++++++++++++++ Modules/Client/Routes/api.php | 2 + .../Transformers/DashboardResources.php | 1 + .../Client/Transformers/MemberResources.php | 25 ++++ app/Models/Corporate.php | 8 +- app/Models/CorporateEmployee.php | 5 + app/Models/Member.php | 10 ++ .../src/pages/Dashboard/Dashboard.tsx | 5 +- .../src/sections/dashboard/CardBalance.tsx | 2 +- .../src/sections/dashboard/TableList.tsx | 97 +++++++++----- 11 files changed, 236 insertions(+), 41 deletions(-) create mode 100755 Modules/Client/Http/Controllers/Api/DivisionController.php create mode 100644 Modules/Client/Transformers/MemberResources.php diff --git a/Modules/Client/Http/Controllers/Api/DashboardController.php b/Modules/Client/Http/Controllers/Api/DashboardController.php index a655aae9..4aea3e51 100755 --- a/Modules/Client/Http/Controllers/Api/DashboardController.php +++ b/Modules/Client/Http/Controllers/Api/DashboardController.php @@ -17,7 +17,7 @@ class DashboardController extends Controller public function index(Request $request) { $user = Auth::user(); - $data = DashboardResources::make($user->managedCorporates()->where('active', 1)->with('currentPolicy', 'employees')->first()); + $data = DashboardResources::make($user->managedCorporates()->where('active', 1)->with('currentPolicy')->first()); return response()->json($data); } diff --git a/Modules/Client/Http/Controllers/Api/DivisionController.php b/Modules/Client/Http/Controllers/Api/DivisionController.php new file mode 100755 index 00000000..3068f338 --- /dev/null +++ b/Modules/Client/Http/Controllers/Api/DivisionController.php @@ -0,0 +1,120 @@ +managedCorporates()->where('active', 1)->first(); + + $benefits = CorporateDivision::query() + ->where('corporate_id', $corporate->id) + ->get(['id', 'name']); + + return $benefits; + } + + /** + * Show the form for creating a new resource. + * @return Renderable + */ + public function create() + { + return view('internal::create'); + } + + /** + * Store a newly created resource in storage. + * @param Request $request + * @return Renderable + */ + public function store(Request $request, $corporate_id) + { + $request->validate([ + 'code' => [ + 'required', + ], + 'name' => 'required' + ]); + + $newCorporatePlan = CorporateDivision::create([ + 'corporate_id' => $corporate_id, + 'code' => $request->code, + 'name' => $request->name, + ]); + + return $newCorporatePlan; + } + + /** + * Show the specified resource. + * @param int $id + * @return Renderable + */ + public function show($id) + { + return view('internal::show'); + } + + /** + * Show the form for editing the specified resource. + * @param int $id + * @return Renderable + */ + public function edit($corporate_id, $id) + { + $corporatePlan = CorporateDivision::findOrFail($id); + + return $corporatePlan; + } + + /** + * Update the specified resource in storage. + * @param Request $request + * @param int $id + * @return Renderable + */ + public function update(Request $request, $corporate_id, $id) + { + $corporatePlan = CorporateDivision::findOrFail($id); + $request->validate([ + 'code' => [ + 'required', + Rule::unique('corporate_plans')->where('corporate_id', $corporate_id)->ignore($corporatePlan->id) + ], + 'name' => 'required' + ]); + + $corporatePlan->fill([ + 'code' => $request->code, + 'name' => $request->name, + 'active' => $request->active, + ])->save(); + + return $corporatePlan; + } + + /** + * Remove the specified resource from storage. + * @param int $id + * @return Renderable + */ + public function destroy($id) + { + // + } +} diff --git a/Modules/Client/Routes/api.php b/Modules/Client/Routes/api.php index 78d6030d..badaf40d 100755 --- a/Modules/Client/Routes/api.php +++ b/Modules/Client/Routes/api.php @@ -3,6 +3,7 @@ use Modules\Client\Http\Controllers\Api\AuthController; use Modules\Client\Http\Controllers\Api\CorporateController; use Modules\Client\Http\Controllers\Api\DashboardController; +use Modules\Client\Http\Controllers\Api\DivisionController; use Modules\Client\Http\Controllers\Api\MemberController; use Modules\Client\Http\Controllers\Api\UserController; @@ -32,6 +33,7 @@ Route::prefix('client')->group(function () { Route::get('dashboard', [DashboardController::class, 'index']); Route::get('corporate', [CorporateController::class, 'index']); Route::get('corporate/{corporate_id}', [CorporateController::class, 'show']); + Route::get('division', [DivisionController::class, 'index']); Route::get('members', [MemberController::class, 'index']); }); }); diff --git a/Modules/Client/Transformers/DashboardResources.php b/Modules/Client/Transformers/DashboardResources.php index 37b7dc2e..a09b6bda 100644 --- a/Modules/Client/Transformers/DashboardResources.php +++ b/Modules/Client/Transformers/DashboardResources.php @@ -2,6 +2,7 @@ namespace Modules\Client\Transformers; +use App\Helpers\Helper; use Illuminate\Http\Resources\Json\JsonResource; class DashboardResources extends JsonResource diff --git a/Modules/Client/Transformers/MemberResources.php b/Modules/Client/Transformers/MemberResources.php new file mode 100644 index 00000000..384998e4 --- /dev/null +++ b/Modules/Client/Transformers/MemberResources.php @@ -0,0 +1,25 @@ + $this->member_id, + 'full_name' => $this->full_name, + 'division' => $this->division->name, + 'employeeLimit' => '', + 'status' => $this->active + ]; + } +} diff --git a/app/Models/Corporate.php b/app/Models/Corporate.php index 04ad740f..f19930c3 100755 --- a/app/Models/Corporate.php +++ b/app/Models/Corporate.php @@ -44,10 +44,10 @@ class Corporate extends Model public function currentPolicy() { return $this->hasOne(CorporatePolicy::class) - // ->where('start', '<=', now()) - // ->where('end', '>=', now()) - ->where('active', true) - ->latestOfMany(); + // ->where('start', '<=', now()) + // ->where('end', '>=', now()) + ->where('active', true) + ->latestOfMany(); } public function corporatePlans() diff --git a/app/Models/CorporateEmployee.php b/app/Models/CorporateEmployee.php index 6263d027..5c0ea63a 100755 --- a/app/Models/CorporateEmployee.php +++ b/app/Models/CorporateEmployee.php @@ -19,4 +19,9 @@ class CorporateEmployee extends Model 'nik', 'status' ]; + + public function division() + { + return $this->belongsTo(CorporateDivision::class, 'division_id'); + } } diff --git a/app/Models/Member.php b/app/Models/Member.php index 8d9dcd44..5737fb1e 100755 --- a/app/Models/Member.php +++ b/app/Models/Member.php @@ -182,4 +182,14 @@ class Member extends Model // }); }); } + + // public function corporateEmployee() + // { + // return $this->hasOne(CorporateEmployee::class, 'member_id'); + // } + + public function division() + { + return $this->hasOneThrough(CorporateDivision::class, CorporateEmployee::class, 'member_id', 'id', 'id', 'division_id'); + } } diff --git a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx index d20e4870..7cd89571 100755 --- a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx +++ b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx @@ -59,8 +59,9 @@ export default function Dashboard() { const { user } = useAuth(); // @ts-ignore - const [corporateValue, setCorporateValue] = useState(`1`); + const [corporateValue, setCorporateValue] = useState(`${user.corporate.id}`); const [corporateData, setCorporateData] = useState([]); + const [tableData, setTableData] = useState([]); const [policyData, setPolicyData] = useState({ myLimit: { balance: 0, @@ -82,6 +83,8 @@ export default function Dashboard() { const corporates = await axios.get('corporate'); const dashboard = await axios.get('dashboard'); + console.log(dashboard); + setCorporateData(corporates.data); setPolicyData(dashboard.data.policy); })(); diff --git a/frontend/client-portal/src/sections/dashboard/CardBalance.tsx b/frontend/client-portal/src/sections/dashboard/CardBalance.tsx index 93adf299..bd14afea 100644 --- a/frontend/client-portal/src/sections/dashboard/CardBalance.tsx +++ b/frontend/client-portal/src/sections/dashboard/CardBalance.tsx @@ -127,7 +127,7 @@ export default function CardBalance(props: CardBalanceProps) { - {lockLimit ? lockLimit.balance : 0} + {lockLimit ? lockLimit.balance : 0} / {myLimit ? myLimit.total : 0} diff --git a/frontend/client-portal/src/sections/dashboard/TableList.tsx b/frontend/client-portal/src/sections/dashboard/TableList.tsx index 4073d089..b8f4a08f 100755 --- a/frontend/client-portal/src/sections/dashboard/TableList.tsx +++ b/frontend/client-portal/src/sections/dashboard/TableList.tsx @@ -14,12 +14,16 @@ import { IconButton, Card, Grid, - Autocomplete, + FormControl, + InputLabel, + Select, + MenuItem, + SelectChangeEvent, } from '@mui/material'; import { visuallyHidden } from '@mui/utils'; import { Add as AddIcon } from '@mui/icons-material'; /* ---------------------------------- axios --------------------------------- */ -import axios from 'axios'; +import axios from '../../utils/axios'; /* ---------------------------------- react --------------------------------- */ import { useEffect, useRef, useState } from 'react'; /* -------------------------------- component ------------------------------- */ @@ -63,6 +67,11 @@ interface EnhancedTableProps { order: Order; orderBy: string; } + +type DivisionDataProps = { + id: number; + name: string; +}; /* -------------------------------------------------------------------------- */ /* -------------------------- enchanced table head -------------------------- */ @@ -140,13 +149,12 @@ function EnhancedTableHead({ order, orderBy, onRequestSort }: EnhancedTableProps } /* -------------------------------------------------------------------------- */ -export default function TableList() { +export default function TableList(props: any) { const [order, setOrder] = useState('asc'); const [orderBy, setOrderBy] = useState('name'); - const [customSearchParams, setCustomSearchParams] = useSearchParams(); + const [searchParams, setSearchParams] = useSearchParams(); const [isLoading, setIsLoading] = useState(true); const [dataTable, setDataTable] = useState([]); - const [dataDivision, setDataDivision] = useState([]); const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(10); const [appliedParams, setAppliedParams] = useState({}); @@ -167,7 +175,7 @@ export default function TableList() { setOrder(isAsc ? 'desc' : 'asc'); setOrderBy(property); const params = Object.fromEntries([ - ...customSearchParams.entries(), + ...searchParams.entries(), ['order', isAsc ? 'desc' : 'asc'], ['orderBy', property], ]); @@ -177,9 +185,24 @@ export default function TableList() { /* ----------------------------- Field Container ---------------------------- */ /* ----------------------------- division field ----------------------------- */ - const optionDivisions = ['All']; + const [divisionValue, setDivisionValue] = useState('all'); + const [divisionData, setDivisionData] = useState([]); - const [value, setValue] = useState(optionDivisions[0]); + const handleDivisionChange = (event: SelectChangeEvent) => { + setDivisionValue(event.target.value as string); + + if (event.target.value === 'all') { + searchParams.delete('division'); + const params = Object.fromEntries([...searchParams.entries()]); + setAppliedParams(params); + } else { + const params = Object.fromEntries([ + ...searchParams.entries(), + ['division', event.target.value as string], + ]); + setAppliedParams(params); + } + }; /* -------------------------------------------------------------------------- */ /* ------------------------------ Search field ------------------------------ */ @@ -188,7 +211,7 @@ export default function TableList() { const handleSearchSubmit = async (event: React.FormEvent) => { event.preventDefault(); setIsLoading(true); - const params = Object.fromEntries([...customSearchParams.entries(), ['search', searchText]]); + const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]); await new Promise((resolve) => setTimeout(resolve, 500)); setAppliedParams(params); setIsLoading(false); @@ -223,12 +246,12 @@ export default function TableList() { newPage: number ) => { setIsLoading(true); - const params = Object.fromEntries([...customSearchParams.entries(), ['page', newPage + 1]]); + const params = Object.fromEntries([...searchParams.entries(), ['page', newPage + 1]]); setPage(newPage); await new Promise((resolve) => setTimeout(resolve, 500)); setAppliedParams(params); setIsLoading(false); - // setCustomSearchParams.set('page', newPage + 1); + // setSearchParams.set('page', newPage + 1); }; /* -------------------------------------------------------------------------- */ @@ -237,7 +260,7 @@ export default function TableList() { setIsLoading(true); setPage(0); const params = Object.fromEntries([ - ...customSearchParams.entries(), + ...searchParams.entries(), ['per_page', parseInt(event.target.value, 10)], ]); setRowsPerPage(parseInt(event.target.value, 10)); @@ -252,29 +275,27 @@ export default function TableList() { (async () => { setIsLoading(true); + const division = await axios.get('/division'); + setDivisionData(division.data); + const params = Object.keys(appliedParams).length !== 0 ? appliedParams - : Object.fromEntries([ - ...customSearchParams.entries(), - ['order', order], - ['orderBy', orderBy], - ]); + : Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]); - const response = await axios.get('http://localhost:8001/api/dashboard', { + const response = await axios.get('/dashboard', { params: params, }); - const division = await axios.get('http://localhost:8001/api/division'); - setDataDivision(division.data); + console.log(response); - setCustomSearchParams(params); - setDataTable(response.data.data); - setPaginationTable(response.data.meta); - setRowsPerPage(response.data.meta.per_page); + setSearchParams(params); + // setDataTable(response.data.data); + // setPaginationTable(response.data.meta); + // setRowsPerPage(response.data.meta.per_page); setIsLoading(false); })(); - }, [appliedParams, customSearchParams, order, orderBy, setCustomSearchParams]); + }, [appliedParams, searchParams, order, orderBy, setSearchParams]); return ( @@ -283,15 +304,23 @@ export default function TableList() { - { - setValue(newValue); - }} - renderInput={(params) => } - /> + + Division + +
From 0c9362334c46a50d52590c6c682bb7f987f9725e Mon Sep 17 00:00:00 2001 From: Muhammad Fajar Date: Thu, 8 Dec 2022 08:51:25 +0700 Subject: [PATCH 11/11] add switch corporate & fix table & fix policy --- ...ller.php => CorporateManageController.php} | 4 +- ...ller.php => CorporatePolicyController.php} | 4 +- .../Controllers/Api/DivisionController.php | 12 +- .../Http/Controllers/Api/MemberController.php | 35 +++--- Modules/Client/Routes/api.php | 22 ++-- .../Client/Transformers/MemberResources.php | 8 +- app/Models/Member.php | 5 - .../src/contexts/UserCurrentCorporate.tsx | 8 ++ .../dashboard/header/CorporatePopover.tsx | 48 ++++++++ .../src/layouts/dashboard/header/index.tsx | 2 + .../src/layouts/dashboard/index.tsx | 36 ++++-- .../src/pages/Dashboard/Dashboard.tsx | 89 +++++---------- .../client-portal/src/pages/auth/Login.tsx | 6 +- .../sections/auth/login/VerifyCodeForm.tsx | 8 +- .../src/sections/dashboard/CardBalance.tsx | 6 +- .../src/sections/dashboard/TableList.tsx | 104 +++++++++++++----- .../client-portal/src/utils/formatNumber.ts | 4 + 17 files changed, 227 insertions(+), 174 deletions(-) rename Modules/Client/Http/Controllers/Api/{CorporateController.php => CorporateManageController.php} (93%) rename Modules/Client/Http/Controllers/Api/{DashboardController.php => CorporatePolicyController.php} (96%) create mode 100755 frontend/client-portal/src/contexts/UserCurrentCorporate.tsx create mode 100755 frontend/client-portal/src/layouts/dashboard/header/CorporatePopover.tsx diff --git a/Modules/Client/Http/Controllers/Api/CorporateController.php b/Modules/Client/Http/Controllers/Api/CorporateManageController.php similarity index 93% rename from Modules/Client/Http/Controllers/Api/CorporateController.php rename to Modules/Client/Http/Controllers/Api/CorporateManageController.php index 6cde4742..efd87ce6 100755 --- a/Modules/Client/Http/Controllers/Api/CorporateController.php +++ b/Modules/Client/Http/Controllers/Api/CorporateManageController.php @@ -7,7 +7,7 @@ use Illuminate\Http\Request; use Illuminate\Routing\Controller; use Illuminate\Support\Facades\Auth; -class CorporateController extends Controller +class CorporateManageController extends Controller { /** * Display a listing of the resource. @@ -18,8 +18,6 @@ class CorporateController extends Controller $userLogin = Auth::user(); $corporate = $userLogin->managedCorporates()->select(['corporates.id', 'corporates.name'])->get(); - // corporate policy, all member list, notification - return response()->json($corporate); } diff --git a/Modules/Client/Http/Controllers/Api/DashboardController.php b/Modules/Client/Http/Controllers/Api/CorporatePolicyController.php similarity index 96% rename from Modules/Client/Http/Controllers/Api/DashboardController.php rename to Modules/Client/Http/Controllers/Api/CorporatePolicyController.php index 441c97af..45eba51d 100755 --- a/Modules/Client/Http/Controllers/Api/DashboardController.php +++ b/Modules/Client/Http/Controllers/Api/CorporatePolicyController.php @@ -8,7 +8,7 @@ use Illuminate\Routing\Controller; use Illuminate\Support\Facades\Auth; use Modules\Client\Transformers\DashboardResources; -class DashboardController extends Controller +class CorporatePolicyController extends Controller { /** * Display a listing of the resource. @@ -20,7 +20,7 @@ class DashboardController extends Controller $currentCorporate = $user->managedCorporates() ->with(['currentPolicy', 'employees']) ->find($corporate_id); - + $data = DashboardResources::make($currentCorporate); return response()->json($data); diff --git a/Modules/Client/Http/Controllers/Api/DivisionController.php b/Modules/Client/Http/Controllers/Api/DivisionController.php index 3068f338..0ddfde4b 100755 --- a/Modules/Client/Http/Controllers/Api/DivisionController.php +++ b/Modules/Client/Http/Controllers/Api/DivisionController.php @@ -15,17 +15,13 @@ class DivisionController extends Controller * Display a listing of the resource. * @return Renderable */ - public function index(Request $request) + public function index(Request $request, $corporate_id) { - $user = Auth::user(); - - $corporate = $user->managedCorporates()->where('active', 1)->first(); - - $benefits = CorporateDivision::query() - ->where('corporate_id', $corporate->id) + $division = CorporateDivision::query() + ->where('corporate_id', $corporate_id) ->get(['id', 'name']); - return $benefits; + return response()->json($division); } /** diff --git a/Modules/Client/Http/Controllers/Api/MemberController.php b/Modules/Client/Http/Controllers/Api/MemberController.php index 9897450d..2283fc3f 100755 --- a/Modules/Client/Http/Controllers/Api/MemberController.php +++ b/Modules/Client/Http/Controllers/Api/MemberController.php @@ -7,6 +7,7 @@ use App\Models\Member; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Modules\Client\Transformers\MemberResources; class MemberController extends Controller { @@ -14,29 +15,25 @@ class MemberController extends Controller * Display a listing of the resource. * @return Renderable */ - public function index(Request $request) + public function index(Request $request, $corporate_id) { - $user = auth()->user(); - - $corporate = $user->managedCorporates()->first(); - // $plans = + $limit = $request->has('per_page') ? $request->per_page : 10; $members = Member::query() - ->whereHas('employeds', function($corporateEmployee) use ($corporate) { - $corporateEmployee->where('corporate_id', $corporate->id); - }); - if ($request->has('search')) { - $members - ->where('member_id', 'like', "%" . $request->search . "%") - ->orWhere('payor_id', 'like', "%" . $request->search . "%") - ->orWhere('name', 'like', "%" . $request->search . "%"); - } + ->whereHas('employeds', function ($corporateEmployee) use ($corporate_id) { + $corporateEmployee->where('corporate_id', $corporate_id); + })->when($request->input('division'), function ($division, $division_id) { + $division->whereHas('division', function ($corporateEmployee) use ($division_id) { + $corporateEmployee->where('division_id', $division_id); + }); + })->when($request->input('search'), function ($query, $search) { + $query->where('member_id', 'like', "%" . $search . "%") + ->orWhere('name', 'like', "%" . $search . "%"); + })->when($request->has('orderBy'), function ($query) use ($request) { + $query->orderBy($request->orderBy, $request->order); + })->paginate($limit); - $members = $members->paginate(); - - return response()->json([ - 'members' => Helper::paginateResources($members) - ]); + return response()->json(Helper::paginateResources(MemberResources::collection($members))); } /** diff --git a/Modules/Client/Routes/api.php b/Modules/Client/Routes/api.php index 30810f4c..533a78d6 100755 --- a/Modules/Client/Routes/api.php +++ b/Modules/Client/Routes/api.php @@ -1,8 +1,8 @@ group(function () { Route::middleware('auth:sanctum')->group(function () { Route::post('logout', [AuthController::class, 'logout'])->name('logout'); - Route::get('user', [UserController::class, 'index']); + Route::get('user', [UserController::class, 'index']); - Route::prefix('{corporate_id}')->group(function() { - Route::get('asd', function ($corporate_id) { - return $corporate_id; + Route::get('corporate-manage', [CorporateManageController::class, 'index']); + Route::prefix('{corporate_id}')->group(function () { + Route::get('policy', [CorporatePolicyController::class, 'index']); + Route::get('division', [DivisionController::class, 'index']); + Route::get('members', [MemberController::class, 'index']); }); - - - Route::get('dashboard', [DashboardController::class, 'index']); - Route::get('corporate', [CorporateController::class, 'index']); - Route::get('corporate/{corporate_id}', [CorporateController::class, 'show']); - Route::get('division', [DivisionController::class, 'index']); - Route::get('members', [MemberController::class, 'index']); - }); }); }); diff --git a/Modules/Client/Transformers/MemberResources.php b/Modules/Client/Transformers/MemberResources.php index 384998e4..6a25d1fd 100644 --- a/Modules/Client/Transformers/MemberResources.php +++ b/Modules/Client/Transformers/MemberResources.php @@ -16,9 +16,13 @@ class MemberResources extends JsonResource { return [ 'memberId' => $this->member_id, - 'full_name' => $this->full_name, + 'fullName' => $this->full_name, 'division' => $this->division->name, - 'employeeLimit' => '', + 'limit' => [ + 'current' => 2000000, + 'total' => 4000000, + 'percentage' => (2000000 / 4000000) * 100 + ], 'status' => $this->active ]; } diff --git a/app/Models/Member.php b/app/Models/Member.php index 5737fb1e..afe28934 100755 --- a/app/Models/Member.php +++ b/app/Models/Member.php @@ -183,11 +183,6 @@ class Member extends Model }); } - // public function corporateEmployee() - // { - // return $this->hasOne(CorporateEmployee::class, 'member_id'); - // } - public function division() { return $this->hasOneThrough(CorporateDivision::class, CorporateEmployee::class, 'member_id', 'id', 'id', 'division_id'); diff --git a/frontend/client-portal/src/contexts/UserCurrentCorporate.tsx b/frontend/client-portal/src/contexts/UserCurrentCorporate.tsx new file mode 100755 index 00000000..5f1ffcdc --- /dev/null +++ b/frontend/client-portal/src/contexts/UserCurrentCorporate.tsx @@ -0,0 +1,8 @@ +import { createContext } from 'react'; + +const UserCurrentCorporateContext = createContext({ + corporateValue: '', + setCorporateValue: (value: string) => Promise, +}); + +export { UserCurrentCorporateContext }; diff --git a/frontend/client-portal/src/layouts/dashboard/header/CorporatePopover.tsx b/frontend/client-portal/src/layouts/dashboard/header/CorporatePopover.tsx new file mode 100755 index 00000000..331af30f --- /dev/null +++ b/frontend/client-portal/src/layouts/dashboard/header/CorporatePopover.tsx @@ -0,0 +1,48 @@ +import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent } from '@mui/material'; +import { useContext, useEffect, useState } from 'react'; +import { UserCurrentCorporateContext } from '../../../contexts/UserCurrentCorporate'; +import axios from '../../../utils/axios'; + +/* ---------------------------------- types --------------------------------- */ +type CorporateDataProps = { + id: number; + name: string; +}; +/* -------------------------------------------------------------------------- */ + +export default function CorporatePopover() { + const { corporateValue, setCorporateValue } = useContext(UserCurrentCorporateContext); + const [corporateData, setCorporateData] = useState([]); + + const handleCorporateChange = (event: SelectChangeEvent) => { + setCorporateValue(event.target.value as string); + }; + + useEffect(() => { + (async () => { + // @ts-ignore + const corporateManages = await axios.get(`/corporate-manage`); + setCorporateData(corporateManages.data); + })(); + }, []); + + return ( + + Corporate + + + ); +} diff --git a/frontend/client-portal/src/layouts/dashboard/header/index.tsx b/frontend/client-portal/src/layouts/dashboard/header/index.tsx index f39781e7..4b1a488f 100755 --- a/frontend/client-portal/src/layouts/dashboard/header/index.tsx +++ b/frontend/client-portal/src/layouts/dashboard/header/index.tsx @@ -18,6 +18,7 @@ import AccountPopover from './AccountPopover'; import LanguagePopover from './LanguagePopover'; import ContactsPopover from './ContactsPopover'; import NotificationsPopover from './NotificationsPopover'; +import CorporatePopover from './CorporatePopover'; // ---------------------------------------------------------------------- @@ -92,6 +93,7 @@ export default function DashboardHeader({ + diff --git a/frontend/client-portal/src/layouts/dashboard/index.tsx b/frontend/client-portal/src/layouts/dashboard/index.tsx index 569ddb4c..b0426c7a 100755 --- a/frontend/client-portal/src/layouts/dashboard/index.tsx +++ b/frontend/client-portal/src/layouts/dashboard/index.tsx @@ -13,6 +13,9 @@ import { HEADER, NAVBAR } from '../../config'; import DashboardHeader from './header'; import NavbarVertical from './navbar/NavbarVertical'; import NavbarHorizontal from './navbar/NavbarHorizontal'; +import useLocalStorage from '../../hooks/useLocalStorage'; +import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; +import useAuth from '../../hooks/useAuth'; // ---------------------------------------------------------------------- @@ -47,6 +50,7 @@ export default function DashboardLayout() { const { collapseClick, isCollapse } = useCollapseDrawer(); const { themeLayout } = useSettings(); + const { user } = useAuth(); const isDesktop = useResponsive('up', 'lg'); @@ -54,6 +58,12 @@ export default function DashboardLayout() { const verticalLayout = themeLayout === 'vertical'; + const [corporateValue, setCorporateValue] = useLocalStorage( + 'corporateValue', + `${user.corporate.id}` + ); + const value = { corporateValue, setCorporateValue }; + if (verticalLayout) { return ( <> @@ -86,19 +96,21 @@ export default function DashboardLayout() { } return ( - - setOpen(true)} /> + + + setOpen(true)} /> - setOpen(false)} /> + setOpen(false)} /> - - - - + + + + + ); } diff --git a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx index d9082d08..1cd1f3dc 100755 --- a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx +++ b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx @@ -1,13 +1,5 @@ // @mui -import { - Typography, - Container, - Grid, - FormControl, - InputLabel, - MenuItem, - Select, -} from '@mui/material'; +import { Typography, Container, Grid } from '@mui/material'; // hooks import useSettings from '../../hooks/useSettings'; // components @@ -16,11 +8,10 @@ import Page from '../../components/Page'; import CardNotification from '../../sections/dashboard/CardNotification'; import CardBalance from '../../sections/dashboard/CardBalance'; import TableList from '../../sections/dashboard/TableList'; -import { useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; import axios from '../../utils/axios'; import { Stack } from '@mui/system'; -import { SelectChangeEvent } from '@mui/material/Select'; -import useAuth from '../../hooks/useAuth'; +import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; // ---------------------------------------------------------------------- @@ -35,12 +26,7 @@ const itemList = [ /* ---------------------------------- types --------------------------------- */ -type CorporateDataProps = { - id: number; - name: string; -}; - -type CardBalanceProps = { +type PolicyProps = { myLimit: { balance: number; total: number; @@ -54,42 +40,35 @@ type CardBalanceProps = { /* -------------------------------------------------------------------------- */ +/* ------------------------------ default data ------------------------------ */ +const defaultPolicyData = { + myLimit: { + balance: 0, + total: 0, + percentage: 0, + }, + lockLimit: { + balance: 0, + percentage: 0, + }, +}; +/* -------------------------------------------------------------------------- */ + export default function Dashboard() { const { themeStretch } = useSettings(); - const { user } = useAuth(); - const [currentCorporate, setCurrentCorporate] = useState({id: 1}); + const { corporateValue } = useContext(UserCurrentCorporateContext); - // @ts-ignore - const [corporateValue, setCorporateValue] = useState(`${user.corporate.id}`); - const [corporateData, setCorporateData] = useState([]); - const [tableData, setTableData] = useState([]); - const [policyData, setPolicyData] = useState({ - myLimit: { - balance: 0, - total: 0, - percentage: 0, - }, - lockLimit: { - balance: 0, - percentage: 0, - }, - }); - - const handleCorporateChange = (event: SelectChangeEvent) => { - setCorporateValue(event.target.value as string); - }; + // const [tableData, setTableData] = useState([]); + const [policyData, setPolicyData] = useState(defaultPolicyData); useEffect(() => { (async () => { - const corporates = await axios.get(`${currentCorporate.id}/dashboard`); - const dashboard = await axios.get(`${currentCorporate.id}/dashboard`); - - console.log(dashboard); - - setCorporateData(corporates.data); + setPolicyData(defaultPolicyData); + await new Promise((resolve) => setTimeout(resolve, 250)); + const dashboard = await axios.get(`${corporateValue}/policy`); setPolicyData(dashboard.data.policy); })(); - }, [user, corporateValue]); + }, [corporateValue]); return ( @@ -98,24 +77,6 @@ export default function Dashboard() { Dashboard - - - Corporate - - diff --git a/frontend/client-portal/src/pages/auth/Login.tsx b/frontend/client-portal/src/pages/auth/Login.tsx index b0e37c57..78694ecd 100755 --- a/frontend/client-portal/src/pages/auth/Login.tsx +++ b/frontend/client-portal/src/pages/auth/Login.tsx @@ -83,11 +83,7 @@ export default function Login() { - + Tidak mendapatkan kode? diff --git a/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx b/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx index 7580971d..97e797b5 100755 --- a/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx +++ b/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx @@ -17,8 +17,6 @@ import useAuth from '../../../hooks/useAuth'; type VerifyCodeFormProps = { emailOrPhone: string; - setEmailOrPhoneForm: Function; - setLoginOrVerifyCode: Function; }; type FormValuesProps = { @@ -39,11 +37,7 @@ type responseProps = { /* -------------------------------------------------------------------------- */ -export default function VerifyCodeForm({ - emailOrPhone, - setEmailOrPhoneForm, - setLoginOrVerifyCode, -}: VerifyCodeFormProps) { +export default function VerifyCodeForm({ emailOrPhone }: VerifyCodeFormProps) { const navigate = useNavigate(); const { validateOtp } = useAuth(); const { enqueueSnackbar } = useSnackbar(); diff --git a/frontend/client-portal/src/sections/dashboard/CardBalance.tsx b/frontend/client-portal/src/sections/dashboard/CardBalance.tsx index bd14afea..e2ef813a 100644 --- a/frontend/client-portal/src/sections/dashboard/CardBalance.tsx +++ b/frontend/client-portal/src/sections/dashboard/CardBalance.tsx @@ -13,7 +13,7 @@ import Iconify from '../../components/Iconify'; // React import { useState } from 'react'; // utils -import { fCurrency } from '../../utils/formatNumber'; +import { fCurrency, fSplit } from '../../utils/formatNumber'; /* -------------------------------- sections -------------------------------- */ import DialogTopUpLimit from './DialogTopUpLimit'; import DialogClaimSubmitMember from './DialogClaimSubmitMember'; @@ -97,7 +97,7 @@ export default function CardBalance(props: CardBalanceProps) { {fCurrency(myLimit ? myLimit.balance : 0)} - / {myLimit ? myLimit.total : 0} + / {fSplit(myLimit ? myLimit.total : 0)} @@ -127,7 +127,7 @@ export default function CardBalance(props: CardBalanceProps) { - {lockLimit ? lockLimit.balance : 0} / {myLimit ? myLimit.total : 0} + {fSplit(lockLimit ? lockLimit.balance : 0)} / {fSplit(myLimit ? myLimit.total : 0)} diff --git a/frontend/client-portal/src/sections/dashboard/TableList.tsx b/frontend/client-portal/src/sections/dashboard/TableList.tsx index b8f4a08f..a263765f 100755 --- a/frontend/client-portal/src/sections/dashboard/TableList.tsx +++ b/frontend/client-portal/src/sections/dashboard/TableList.tsx @@ -1,4 +1,5 @@ /* ---------------------------------- @mui ---------------------------------- */ +import { styled } from '@mui/material/styles'; import { Paper, Table, @@ -19,19 +20,25 @@ import { Select, MenuItem, SelectChangeEvent, + Stack, + Typography, + LinearProgress, + linearProgressClasses, } from '@mui/material'; import { visuallyHidden } from '@mui/utils'; import { Add as AddIcon } from '@mui/icons-material'; /* ---------------------------------- axios --------------------------------- */ import axios from '../../utils/axios'; /* ---------------------------------- react --------------------------------- */ -import { useEffect, useRef, useState } from 'react'; +import { useContext, useEffect, useRef, useState } from 'react'; /* -------------------------------- component ------------------------------- */ import Iconify from '../../components/Iconify'; import BaseTablePagination from '../../components/BaseTablePagination'; /* ---------------------------------- theme --------------------------------- */ import palette from '../../theme/palette'; import { useSearchParams } from 'react-router-dom'; +import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; +import { fSplit } from '../../utils/formatNumber'; /* ---------------------------------- types --------------------------------- */ type PaginationTableProps = { @@ -46,11 +53,15 @@ type PaginationTableProps = { }; type DataTableProps = { - name: string; - member_id: string; + fullName: string; + memberId: string; division: string; - limit: string; - status: string; + limit: { + current: number; + total: number; + percentage: number; + }; + status: number; }; type Order = 'asc' | 'desc'; @@ -74,6 +85,20 @@ type DivisionDataProps = { }; /* -------------------------------------------------------------------------- */ +/* --------------------------------- styled --------------------------------- */ +const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ + height: 10, + borderRadius: 6, + [`&.${linearProgressClasses.colorPrimary}`]: { + backgroundColor: '#D1F1F1', + }, + [`& .${linearProgressClasses.bar}`]: { + borderRadius: 6, + backgroundColor: theme.palette.primary.main, + }, +})); +/* -------------------------------------------------------------------------- */ + /* -------------------------- enchanced table head -------------------------- */ const headCells: readonly HeadCell[] = [ { @@ -92,16 +117,16 @@ const headCells: readonly HeadCell[] = [ id: 'division', align: 'center', label: 'Divisi', - isSort: true, + isSort: false, }, { id: 'limit', align: 'center', label: 'Limit', - isSort: true, + isSort: false, }, { - id: 'status', + id: 'active', align: 'center', label: 'Status', isSort: true, @@ -150,14 +175,9 @@ function EnhancedTableHead({ order, orderBy, onRequestSort }: EnhancedTableProps /* -------------------------------------------------------------------------- */ export default function TableList(props: any) { - const [order, setOrder] = useState('asc'); - const [orderBy, setOrderBy] = useState('name'); - const [searchParams, setSearchParams] = useSearchParams(); - const [isLoading, setIsLoading] = useState(true); + const { corporateValue } = useContext(UserCurrentCorporateContext); + const [dataTable, setDataTable] = useState([]); - const [page, setPage] = useState(0); - const [rowsPerPage, setRowsPerPage] = useState(10); - const [appliedParams, setAppliedParams] = useState({}); const [paginationTable, setPaginationTable] = useState({ current_page: 0, from: 0, @@ -169,6 +189,15 @@ export default function TableList(props: any) { total: 0, }); + const [isLoading, setIsLoading] = useState(true); + const [searchParams, setSearchParams] = useSearchParams(); + const [appliedParams, setAppliedParams] = useState({}); + + const [order, setOrder] = useState('asc'); + const [orderBy, setOrderBy] = useState('name'); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + /* ------------------------------- handle sort ------------------------------ */ const handleRequestSort = async (event: React.MouseEvent, property: string) => { const isAsc = orderBy === property && order === 'asc'; @@ -211,9 +240,15 @@ export default function TableList(props: any) { const handleSearchSubmit = async (event: React.FormEvent) => { event.preventDefault(); setIsLoading(true); - const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]); + if (searchText === '') { + searchParams.delete('search'); + const params = Object.fromEntries([...searchParams.entries()]); + setAppliedParams(params); + } else { + const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]); + setAppliedParams(params); + } await new Promise((resolve) => setTimeout(resolve, 500)); - setAppliedParams(params); setIsLoading(false); }; /* -------------------------------------------------------------------------- */ @@ -275,7 +310,7 @@ export default function TableList(props: any) { (async () => { setIsLoading(true); - const division = await axios.get('/division'); + const division = await axios.get(`${corporateValue}/division`); setDivisionData(division.data); const params = @@ -283,19 +318,17 @@ export default function TableList(props: any) { ? appliedParams : Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]); - const response = await axios.get('/dashboard', { - params: params, + const corporateMembers = await axios.get(`${corporateValue}/members`, { + params, }); - console.log(response); - setSearchParams(params); - // setDataTable(response.data.data); + setDataTable(corporateMembers.data.data); // setPaginationTable(response.data.meta); // setRowsPerPage(response.data.meta.per_page); setIsLoading(false); })(); - }, [appliedParams, searchParams, order, orderBy, setSearchParams]); + }, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]); return ( @@ -388,12 +421,23 @@ export default function TableList(props: any) { ) : dataTable.length >= 1 ? ( dataTable.map((row: DataTableProps, index) => ( - {row.member_id} - {row.name} + {row.memberId} + {row.fullName} {row.division} - {row.limit} + + + + + {fSplit(row.limit.current)} / {fSplit(row.limit.total)} + + + - {row.status.toLowerCase() === 'active' ? ( + {row.status === 1 ? ( ) : ( )} diff --git a/frontend/client-portal/src/utils/formatNumber.ts b/frontend/client-portal/src/utils/formatNumber.ts index 353c0299..834dd5f4 100755 --- a/frontend/client-portal/src/utils/formatNumber.ts +++ b/frontend/client-portal/src/utils/formatNumber.ts @@ -28,6 +28,10 @@ export function fCurrency(number: string | number) { return numeral(number).format('$0,0'); } +export function fSplit(number: string | number) { + return numeral(number).format('0,0'); +} + export function fPercent(number: number) { return numeral(number / 100).format('0.0%'); }