diff --git a/Modules/Internal/Http/Controllers/Api/DoctorController.php b/Modules/Internal/Http/Controllers/Api/DoctorController.php index dc6c18e7..9310b3c0 100644 --- a/Modules/Internal/Http/Controllers/Api/DoctorController.php +++ b/Modules/Internal/Http/Controllers/Api/DoctorController.php @@ -3,6 +3,7 @@ namespace Modules\Internal\Http\Controllers\Api; use App\Helpers\Helper; +use App\Models\Practitioner; use App\Models\PractitionerRole; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; @@ -15,9 +16,34 @@ class DoctorController extends Controller * Display a listing of the resource. * @return Renderable */ - public function index() + public function index(Request $request) { - $doctors = PractitionerRole::active()->with('practitioner.person', 'organization')->paginate(); + // $doctors = PractitionerRole::active()->with('practitioner.person', 'organization') + // ->when($request->search ?? null, function ($query, $search) { + // $query->whereHas('practitioner.person', function ($person) use ($search) { + // $person->where('name', 'LIKE', '%' . $search . '%'); + // }); + // })->paginate(); + + $doctors = Practitioner::with('person', 'practitionerRoles.organization', 'practitionerRoles.speciality') + ->when($request->search ?? null, function ($query, $search) { + $query->whereHas('person', function ($person) use ($search) { + $person->where('name', 'LIKE', '%' . $search . '%'); + }); + }) + ->when($request->organization_id ?? null, function ($query, $organization_id) { + $query->whereHas('practitionerRoles', function ($practitionerRole) use ($organization_id) { + $practitionerRole->where('organization_id', $organization_id); + }); + }) + ->when($request->speciality_id ?? null, function ($query, $speciality_id) { + $query->whereHas('practitionerRoles', function ($practitionerRole) use ($speciality_id) { + $practitionerRole->where('speciality_id', $speciality_id); + }); + }) + ->paginate(); + + // return $doctors; return response()->json(Helper::paginateResources(DoctorResource::collection($doctors))); diff --git a/Modules/Internal/Http/Controllers/Api/OrganizationController.php b/Modules/Internal/Http/Controllers/Api/OrganizationController.php index 5b1aae0c..a22c1010 100644 --- a/Modules/Internal/Http/Controllers/Api/OrganizationController.php +++ b/Modules/Internal/Http/Controllers/Api/OrganizationController.php @@ -15,12 +15,22 @@ class OrganizationController extends Controller * Display a listing of the resource. * @return Renderable */ - public function index() + public function index(Request $request) { - $organizations = Organization::active()->hospital()->with('currentAddress')->paginate(); + $organizations = Organization::hospital()->with('currentAddress') + ->when($request->search ?? null, function ($query, $search) { + $query->where('name', 'LIKE', '%' . $search . '%'); + }) + ->paginate(); return response()->json(Helper::paginateResources(OrganizationResource::collection($organizations))); } + public function searchOrganization(Request $request) + { + $organizations = Organization::hospital()->get(); + return response()->json(OrganizationResource::collection($organizations)); + } + /** * Show the form for creating a new resource. * @return Renderable diff --git a/Modules/Internal/Http/Controllers/Api/SpecialityController.php b/Modules/Internal/Http/Controllers/Api/SpecialityController.php new file mode 100644 index 00000000..bfc93636 --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/SpecialityController.php @@ -0,0 +1,87 @@ +json($specialiy); + } + + public function searchSpeciality(Request $request) + { + $specialiy = Speciality::get(); + return response()->json($specialiy); + } + + /** + * 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) + { + // + } + + /** + * 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($id) + { + return view('internal::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/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index a20cff35..a4aef83d 100755 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -20,6 +20,7 @@ use Modules\Internal\Http\Controllers\Api\FormulariumController; use Modules\Internal\Http\Controllers\Api\MemberController; use Modules\Internal\Http\Controllers\Api\OrganizationController; use Modules\Internal\Http\Controllers\Api\PlanController; +use Modules\Internal\Http\Controllers\Api\SpecialityController; /* |-------------------------------------------------------------------------- @@ -104,10 +105,15 @@ Route::prefix('internal')->group(function () { Route::post('claims', [ClaimController::class, 'store']); Route::get('claims/{id}', [ClaimController::class, 'show']); Route::post('check-limit', [ClaimController::class, 'checkLimit']); + + Route::get('search-organizations', [OrganizationController::class, 'searchOrganization']); + Route::get('search-specialities', [SpecialityController::class, 'searchSpeciality']); + Route::resource('organizations', OrganizationController::class); + Route::resource('doctors', DoctorController::class); }); - Route::resource('organizations', OrganizationController::class); - Route::resource('doctors', DoctorController::class); + // Route::resource('organizations', OrganizationController::class); + // Route::resource('doctors', DoctorController::class); // Route::get('something', [DiagnosisExclusionController::class, 'index']); }); diff --git a/Modules/Internal/Transformers/DoctorResource.php b/Modules/Internal/Transformers/DoctorResource.php index f14151c3..c5f31fca 100644 --- a/Modules/Internal/Transformers/DoctorResource.php +++ b/Modules/Internal/Transformers/DoctorResource.php @@ -16,20 +16,35 @@ class DoctorResource extends JsonResource { $doctor = [ 'id' => $this->id, - 'his_dokter_id' => $this->meta->DokterID, - 'name' => $this->practitioner->person->name, - 'phone' => $this->practitioner->person->phone, - 'email' => $this->practitioner->person->email, - 'address' => $this->practitioner->person->currentAddress->text, - 'organization' => $this->organization->name, - 'organization_id' => $this->organization->id, - 'specialty' => $this->speciality->name, - 'specialtyI_id' => $this->speciality->id, - 'education' => $this->practitioner->meta->education, - 'experience' => $this->practitioner->meta->work_experience, - 'award' => $this->practitioner->meta->award, - 'keilmuan' => $this->practitioner->meta->Keilmuan, - 'tipe_dokter' => $this->practitioner->meta->tipeDokter, + // 'his_dokter_id' => $this->practitionerRoles->meta, + 'name' => $this->person->name, + 'person_id' => $this->person->id, + 'phone' => $this->person->phone, + 'email' => $this->person->email, + 'gender' => $this->person->gender == "L" ? 'Laki-laki' : 'Perempuan', + 'address' => $this->person->currentAddress->text, + 'organizations' => $this->practitionerRoles->unique('organization_id')->map(function ($practitionerRole) { + return [ + 'organization_id' => $practitionerRole->organization->id, + 'organization_name' => $practitionerRole->organization->name, + ]; + }), + "specialties" => $this->practitionerRoles->unique('speciality_id')->map(function ($practitionerRole) { + return [ + 'specialty_id' => $practitionerRole->speciality->id, + 'specialty_name' => $practitionerRole->speciality->name, + ]; + }), + "departemen" => $this->practitionerRoles->map(function ($practitionerRole) { + return [ + 'departemen_id' => $practitionerRole->meta->DepartemenID, + ]; + }), + 'education' => $this->meta->education, + 'experience' => $this->meta->work_experience, + 'award' => $this->meta->award, + 'keilmuan' => $this->meta->Keilmuan, + 'tipe_dokter' => $this->meta->tipeDokter, ]; diff --git a/Modules/Internal/Transformers/OrganizationResource.php b/Modules/Internal/Transformers/OrganizationResource.php index da8b8e7f..d726d704 100644 --- a/Modules/Internal/Transformers/OrganizationResource.php +++ b/Modules/Internal/Transformers/OrganizationResource.php @@ -15,6 +15,7 @@ class OrganizationResource extends JsonResource public function toArray($request) { $organization = [ + 'id' => $this->id, 'name' => $this->name, 'type' => $this->type, 'code' => $this->code, diff --git a/database/seeders/OrganizationSeeder.php b/database/seeders/OrganizationSeeder.php index 6038f6b7..064d6909 100755 --- a/database/seeders/OrganizationSeeder.php +++ b/database/seeders/OrganizationSeeder.php @@ -17,6 +17,7 @@ class OrganizationSeeder extends Seeder */ public function run() { + $response = Http::get('https://app.primaya.id/temp/organizations'); foreach ($response->json()['organizations'] as $organization) { diff --git a/database/seeders/PractitionerSeeder.php b/database/seeders/PractitionerSeeder.php index f0320469..26014449 100755 --- a/database/seeders/PractitionerSeeder.php +++ b/database/seeders/PractitionerSeeder.php @@ -33,8 +33,6 @@ class PractitionerSeeder extends Seeder // Meta::query()->where('metaable_type', PractitionerRole::class)->forceDelete(); // Address::query()->where('addressable_type', Person::class)->forceDelete(); - - $mapSpecialities = [ 'Akupunktur' => 'SP027', 'Anak' => 'SP001', @@ -125,7 +123,7 @@ class PractitionerSeeder extends Seeder $findSpecialityId = Speciality::where('name', $mapping)->first()->id; $newPerson = Person::updateOrCreate([ - 'nik' => $doctor['NIK'] + 'name' => $doctor['Nama'], ], [ 'nik' => (!empty($doctor['NIK'])) ? $doctor['NIK'] : null, 'name' => $doctor['Nama'], @@ -195,41 +193,83 @@ class PractitionerSeeder extends Seeder // 'value' => $doctor['Description'] // ]); - $newPractitionerRole = PractitionerRole::updateOrCreate([ - 'practitioner_id' => $newPractitioner->id, - 'organization_id' => $organization->id, - 'speciality_id' => $findSpecialityId, - 'active' => 1, - ]); + $ownedDepartemenIDs = $newPractitioner->practitionerRoles->pluck('meta.DepartemenID'); - $this->command->info('Doctor : ' . $DokterID . '-' . $doctor['Nama'] . ' (Spesialisasi : ' . $mapping . ')'); + $DepartemenIDs = $doctor['departementIDs']; + foreach ($DepartemenIDs as $DepartemenID) { + $updatePractitionerRole = $newPractitioner->practitionerRoles->where('meta.DepartemenID', $DepartemenID)->first(); - if (empty($newPractitionerRole->speciality_id)) { - $newPractitionerRole->metas()->updateOrCreate([ - 'type' => 'speciality_code', - ], [ - 'system' => 'primaya-his', - 'type' => 'speciality_code', - 'value' => (!empty($doctor['KodeSpesialisasiID']) && $doctor['KodeSpesialisasiID'] != '-') ? $doctor['KodeSpesialisasiID'] : null - ]); - $newPractitionerRole->metas()->updateOrCreate([ - 'type' => 'speciality', - ], [ - 'system' => 'primaya-his', - 'type' => 'speciality', - 'value' => (!empty($doctor['spesialisasi']['Nama']) && $doctor['spesialisasi']['Nama'] != '-') ? $doctor['spesialisasi']['Nama'] : null - ]); + if (empty($updatePractitionerRole)) { + $updatePractitionerRole = $newPractitioner->practitionerRoles->where('meta.DokterID', $DokterID)->first(); + + if (!empty($updatePractitionerRole->meta->DepartemenID) && $updatePractitionerRole->meta->DepartemenID != $DepartemenID) { + $updatePractitionerRole = null; + } + } + + if ( + isset($updatePractitionerRole) + ) { + + $updatePractitionerRole->metas()->updateOrCreate([ + 'type' => 'DepartemenID', + ], [ + 'type' => 'DepartemenID', + 'system' => 'his', + 'value' => $DepartemenID + ]); + + $newPractitioner->load('practitionerRoles'); + $this->command->info('Updating Practitioner Role : ' . $updatePractitionerRole->id . '-' . $DokterID . '-' . $doctor['Nama'] . '-' . $DepartemenID); + } else { + + $newPractitionerRole = PractitionerRole::Create([ + 'practitioner_id' => $newPractitioner->id, + 'organization_id' => $organization->id, + 'speciality_id' => $findSpecialityId, + 'active' => 1, + ]); + + if (empty($newPractitionerRole->speciality_id)) { + $newPractitionerRole->metas()->updateOrCreate([ + 'type' => 'speciality_code', + ], [ + 'system' => 'primaya-his', + 'type' => 'speciality_code', + 'value' => (!empty($doctor['KodeSpesialisasiID']) && $doctor['KodeSpesialisasiID'] != '-') ? $doctor['KodeSpesialisasiID'] : null + ]); + + $newPractitionerRole->metas()->updateOrCreate([ + 'type' => 'speciality', + ], [ + 'system' => 'primaya-his', + 'type' => 'speciality', + 'value' => (!empty($doctor['spesialisasi']['Nama']) && $doctor['spesialisasi']['Nama'] != '-') ? $doctor['spesialisasi']['Nama'] : null + ]); + } + + $newPractitionerRole->metas()->updateOrCreate([ + 'type' => 'primaya-his', + 'type' => 'DokterID', + ], [ + 'system' => 'primaya-his', + 'type' => 'DokterID', + 'value' => $DokterID + ]); + + $newPractitionerRole->metas()->updateOrCreate([ + 'type' => 'primaya-his', + 'type' => 'DepartemenID', + ], [ + 'system' => 'primaya-his', + 'type' => 'DepartemenID', + 'value' => $DepartemenID + ]); + + $this->command->info('Creating Practitioner Role : ' . $newPractitionerRole->id . '-' . $DokterID . '-' . $doctor['Nama'] . '-' . $DepartemenID); + } } - - $newPractitionerRole->metas()->updateOrCreate([ - 'type' => 'primaya-his', - 'type' => 'DokterID', - ], [ - 'system' => 'primaya-his', - 'type' => 'DokterID', - 'value' => $DokterID - ]); } $hisOrganizationCode = $organization->meta->KodeRS ?? null; diff --git a/frontend/dashboard/src/@types/doctor.tsx b/frontend/dashboard/src/@types/doctor.tsx new file mode 100644 index 00000000..0d2925b4 --- /dev/null +++ b/frontend/dashboard/src/@types/doctor.tsx @@ -0,0 +1,51 @@ +export type Organizations = { + id: number; + code: string; + name: string; + address: string; + type: string; + lat: string; + lng: string; + phone: string; + timezone: string; + active: boolean | number; +}; + +export type PractitionerRole = { + meta: string; + practitioner_id: number; + organization_id: number; + identifier_id: number; + speciality_id: number; + period_start: string; + period_end: string; + active: boolean | number; +}; + +export type Specialities = { + id: number; + code: string; + name: string; +}; + +export type Practitioner = { + id: number; + name: string; + name_prefix: string; + name_suffix: string; + address: string; + birth_date: string; + birth_place: string; + phone: string; + email: string; + gender: string; + description: string; + avatar_url: string; + doctor_id: string; + education: string; + experience: string; + award: string; + active: boolean | number; + organizations?: Organizations[]; + specialities?: Specialities[]; +}; diff --git a/frontend/dashboard/src/@types/organization.tsx b/frontend/dashboard/src/@types/organization.tsx new file mode 100644 index 00000000..d5348677 --- /dev/null +++ b/frontend/dashboard/src/@types/organization.tsx @@ -0,0 +1,51 @@ +export type Organizations = { + id: number; + code: string; + name: string; + address: string; + type: string; + lat: string; + lng: string; + phone: string; + timezone: string; + active: boolean | number; + province_id: number; + city_id: number; + district_id: number; + village_id: number; + postal_code: string; + description: string; + technology: string; + support_services: string; + merchant_code: string; + merchant_key: string; + image_url: string; + region_groups: string; +}; + +export type Provinces = { + id: number; + name: string; + code: string; +}; + +export type Cities = { + id: number; + name: string; + code: string; + province_id: number; +}; + +export type Districts = { + id: number; + name: string; + code: string; + city_id: number; +}; + +export type Villages = { + id: number; + name: string; + code: string; + district_id: number; +}; diff --git a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx index 1072023d..aa12a265 100755 --- a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx +++ b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx @@ -19,9 +19,7 @@ const navConfig = [ // GENERAL // ---------------------------------------------------------------------- { - items: [ - { title: 'Dashboard', path: '/dashboard', icon: ICONS.dashboard }, - ], + items: [{ title: 'Dashboard', path: '/dashboard', icon: ICONS.dashboard }], }, // Membership @@ -37,8 +35,8 @@ const navConfig = [ { title: 'DOCTORS & HOSPITALS', children: [ - { title: 'Doctors', path: '/doctors' }, - { title: 'Hospitals', path: '/hospitals' }, + { title: 'Doctors', path: '/master/doctors' }, + { title: 'Hospitals', path: '/master/hospitals' }, ], }, { @@ -62,16 +60,14 @@ const navConfig = [ }, { title: 'CASE MANAGEMENT', - path: '/claims', + path: '/claims', // children: [ // { title: 'Request', path: '/case-request' }, // ], }, { title: 'CUSTOMER SERVICES', - children: [ - { title: 'Request', path: '/cs-request' }, - ], + children: [{ title: 'Request', path: '/cs-request' }], }, { title: 'USER MANAGEMENT', diff --git a/frontend/dashboard/src/pages/Master/Doctors/Create.tsx b/frontend/dashboard/src/pages/Master/Doctors/Create.tsx new file mode 100644 index 00000000..b0f07577 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Doctors/Create.tsx @@ -0,0 +1,93 @@ +import { useEffect, useState } from 'react'; +import { paramCase } from 'change-case'; +import { useParams, useLocation } from 'react-router-dom'; +// @mui +import { Container, Stack } from '@mui/material'; +import useSettings from '../../../hooks/useSettings'; +import Page from '../../../components/Page'; +import Form from './Form'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import axios from '../../../utils/axios'; +import { Practitioner } from '../../../@types/doctor'; +import ButtonBack from '../../../components/ButtonBack'; + +export default function Create() { + const { themeStretch } = useSettings(); + const { id } = useParams(); + + const isEdit = id ? true : false; + + const [currentPractitioner, setCurrentPractitioner] = useState(); + + useEffect(() => { + if (isEdit) { + axios.get('/doctors/' + id).then((res) => { + setCurrentPractitioner(res.data); + }); + } + }, [id]); + + return ( + + + + + + + +
+ + + ); +} +// const pageTitle = 'Create Data Dokter'; +// return ( +// +// +// + +// +// +// +// +// +// +// +// +// +// ); +// } diff --git a/frontend/dashboard/src/pages/Master/Doctors/Form.tsx b/frontend/dashboard/src/pages/Master/Doctors/Form.tsx new file mode 100644 index 00000000..39885db8 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Doctors/Form.tsx @@ -0,0 +1,260 @@ +import * as Yup from 'yup'; +import { useSnackbar } from 'notistack'; +import { useNavigate } from 'react-router-dom'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import MenuItem from '@mui/material/MenuItem'; + +import Select, { SelectChangeEvent } from '@mui/material/Select'; +import * as React from 'react'; + +// form +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +// @mui +import { styled } from '@mui/material/styles'; +import { LoadingButton } from '@mui/lab'; +import { + Box, + Avatar, + Button, + ButtonGroup, + Card, + FormHelperText, + Grid, + Stack, + Typography, + TextField, + Chip, +} from '@mui/material'; + +import CancelIcon from '@mui/icons-material/Cancel'; + +// components +import { + FormProvider, + RHFTextField, + RHFRadioGroup, + RHFUploadAvatar, + RHFSwitch, + RHFEditor, + RHFDatepicker, + RHFMultiCheckbox, + RHFCheckbox, + RHFCustomMultiCheckbox, +} from '../../../components/hook-form'; +import axios from '../../../utils/axios'; +import { fCurrency } from '../../../utils/formatNumber'; +import { Practitioner } from '../../../@types/doctor'; + +import { Label, Rowing } from '@mui/icons-material'; + +const LabelStyle = styled(Typography)(({ theme }) => ({ + ...theme.typography.subtitle2, + color: theme.palette.text.secondary, + marginBottom: theme.spacing(1), +})); + +const HeaderStyle = styled('header')(({ theme }) => ({ + paddingBottom: theme.spacing(5), + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', +})); + +const Title = styled(Typography)(({ theme }) => ({ + ...theme.typography.h4, + boxShadow: 'none', + // paddingBottom: theme.spacing(3), + fontWeight: 700, + color: '#005B7F', +})); + +interface FormValuesProps extends Partial { + taxes: boolean; + inStock: boolean; +} + +type Props = { + isEdit: boolean; + currentPractitioner?: Practitioner; +}; + +const Span = styled(Typography)(({ theme }) => ({ + boxShadow: 'none', + paddingBottom: theme.spacing(1), +})); + +const Text = styled(Typography)(({ theme }) => ({ + boxShadow: 'none', + paddingBottom: theme.spacing(3), +})); + +export default function PractitionerForm({ isEdit, currentPractitioner }: Props) { + const navigate = useNavigate(); + const [practitioner_group, setPractitionerGroups] = useState([]); + + // const [ errors, setErrors ] = useState<{ [key: string]: string }>({}); + + const { enqueueSnackbar } = useSnackbar(); + + const NewCorporateSchema = Yup.object().shape({ + name: Yup.string().required('Name is required'), + // file: Yup.boolean().required('Corporate Status is required'), + }); + + const defaultValues = useMemo( + () => ({ + id: currentPractitioner?.id, + name: currentPractitioner?.name || '', + address: currentPractitioner?.address || '', + birth_date: currentPractitioner?.birth_date || '', + gender: currentPractitioner?.gender || '', + description: currentPractitioner?.description || '', + birth_place: currentPractitioner?.birth_place || '', + active: currentPractitioner?.active === 1 ? true : false, + avatar_url: currentPractitioner?.avatar_url || '', + doctor_id: currentPractitioner?.doctor_id || '', + organizations: currentPractitioner?.organizations || [], + specialities: currentPractitioner?.specialities || [], + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [currentPractitioner] + ); + + console.log('defaultValues', defaultValues); + + function StatusLabel({ value }: { value: boolean }) { + return ( + + ); + } + const methods = useForm({ + resolver: yupResolver(NewCorporateSchema), + defaultValues, + }); + + const { + reset, + watch, + control, + setValue, + getValues, + setError, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + const values = watch(); + + useEffect(() => { + if (isEdit && currentPractitioner) { + reset(defaultValues); + } + if (!isEdit) { + reset(defaultValues); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isEdit, currentPractitioner]); + + const handleActivate = (event: React.ChangeEvent) => { + setValue('active', event.target.checked); + + console.log('event.target.checked', event.target.checked); + + const formData = new FormData(); + formData.append('active', event.target.checked ? '1' : '0'); + formData.append('_method', 'PUT'); + axios.post('/doctors/' + currentPractitioner?.id ?? '', formData); + + enqueueSnackbar('active Updated Successfully!', { variant: 'success' }); + }; + + return ( + + + + {/* */} + + + + Data Dokter + + + {/* Status Rumah Sakit */} + + + + + Informasi Umum + + + + + Nama Dokter + {currentPractitioner?.name ? currentPractitioner?.name : '-'} + No Telp + {currentPractitioner?.phone ? currentPractitioner?.phone : '-'} + Tempat Lahir + + {currentPractitioner?.birth_place ? currentPractitioner?.birth_place : '-'} + + Alamat + {currentPractitioner?.address ? currentPractitioner?.address : '-'} + + + Jenis Kelamin + {currentPractitioner?.gender ? currentPractitioner?.gender : '-'} + Email + {currentPractitioner?.email ? currentPractitioner?.email : '-'} + Tanggal Lahir + + {currentPractitioner?.birth_date ? currentPractitioner?.birth_date : '-'} + + + + + + Tempat Praktik + {currentPractitioner?.organizations?.map((item, index) => ( + + + + {item.name} + + + + ))} + + + Spesialisasi + {currentPractitioner?.specialities?.map((item, index) => ( + + + + {item.name} + + + + ))} + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Doctors/Index.tsx b/frontend/dashboard/src/pages/Master/Doctors/Index.tsx new file mode 100644 index 00000000..7f495a24 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Doctors/Index.tsx @@ -0,0 +1,35 @@ +import { Card, Grid, Container } from '@mui/material'; +import { useParams } from 'react-router-dom'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import Page from '../../../components/Page'; +import useSettings from '../../../hooks/useSettings'; +import List from './List'; + +export default function Doctors() { + const { themeStretch } = useSettings(); + + const { id } = useParams(); + + const pageTitle = 'Manage Dokter'; + return ( + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Doctors/List.tsx b/frontend/dashboard/src/pages/Master/Doctors/List.tsx new file mode 100644 index 00000000..f311bc2a --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Doctors/List.tsx @@ -0,0 +1,552 @@ +import { + Box, + Button, + Card, + Collapse, + Paper, + Select, + SelectChangeEvent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Typography, + Stack, + ButtonGroup, + Grid, + Chip, + Dialog, + DialogContent, + DialogContentText, + DialogActions, + FormControl, + Autocomplete, + InputAdornment, + IconButton, +} from '@mui/material'; + +import { + Link, + NavLink as RouterLink, + useSearchParams, + useNavigate, + useParams, +} from 'react-router-dom'; +// hooks +import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; +import useSettings from '../../../hooks/useSettings'; +// components +import axios from '../../../utils/axios'; +import { LaravelPaginatedData } from '../../../@types/paginated-data'; +import { Icd } from '../../../@types/diagnosis'; +import BasePagination from '../../../components/BasePagination'; +import { Practitioner } from '../../../@types/doctor'; +import CreateIcon from '@mui/icons-material/Create'; +import { Props } from '../../../components/editor/index'; +import { red } from '@mui/material/colors'; +import { margin, padding } from '@mui/system'; +import { enqueueSnackbar } from 'notistack'; +import { Controller } from 'react-hook-form'; + +import SvgIconStyle from '../../../components/SvgIconStyle'; +import { GridSearchIcon } from '@mui/x-data-grid'; +import { Search } from '@mui/icons-material'; +import { Icon } from '@iconify/react'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; + +// ---------------------------------------------------------------------- + +export default function List() { + // Generate the every row of the table + + const navigate = useNavigate(); + const { organization_id } = useParams(); + const [searchParams, setSearchParams] = useSearchParams(); + const [searchParamsOrganizations, setSearchParamsOrganizations] = useSearchParams(); + const [searchParamsSpecialities, setSearchParamsSpecialities] = useSearchParams(); + const [searchParamsFilter, setSearchParamsFilter] = useSearchParams(); + + function Filter(props: any) { + // SEARCH + const searchInput = useRef(null); + const [seacrhOrganizations, setSearchOrganizations] = useState(''); + const [searchSpecialities, setSearchSpecialities] = useState(''); + const [searchText, setSearchText] = useState(''); + + //handle search + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? ''; + setSearchText(newSearchText); + }; + + const handleSearchOrganizations = (event: any) => { + const newSearchOrganizations = event.id ?? ''; + console.log(newSearchOrganizations); + setSearchOrganizations(newSearchOrganizations); + }; + + const handleSearchSpecialities = (event: any) => { + const newSearchSpecialities = event.id ?? ''; + setSearchSpecialities(newSearchSpecialities); + }; + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + + props.onSearch(searchText, seacrhOrganizations, searchSpecialities); + }; + + const [organization, setOrganization] = useState([]); + const [specialities, setSpecialities] = useState([]); + + useEffect(() => { + axios.get(`/search-organizations`).then((response) => { + setOrganization(response.data); + }); + + axios.get(`/search-specialities`).then((response) => { + setSpecialities(response.data); + }); + + // Trigger First Search + setSearchText(searchParams.get('search') ?? ''); + setSearchOrganizations(searchParamsOrganizations.get('organization_id') ?? ''); + setSearchSpecialities(searchParamsSpecialities.get('speciality_id') ?? ''); + }, []); + + const item = [ + { + id: '', + value: '', + name: 'Semua', + }, + ]; + + //item search + const dataOrganizations = item.concat(organization); + const dataSpecialities = item.concat(specialities); + + return ( + + + + { + if (event.key === 'Enter') { + handleSearchSubmit(event); + } + }} + value={searchText} + InputProps={{ + startAdornment: ( + + + + ), + placeholder: 'Search', + }} + /> + + + v.id == seacrhOrganizations)} + getOptionLabel={(option) => option.name} + onChange={(event, value) => { + handleSearchOrganizations(value); + }} + onKeyDown={(event) => { + if (event.key === 'Enter') { + handleSearchSubmit(event); + } + }} + renderInput={(params) => ( + + )} + /> + + + option.name} + value={dataSpecialities.find((v) => v.id == searchSpecialities)} + onChange={(event, value) => handleSearchSpecialities(value)} + onKeyDown={(event) => { + if (event.key === 'Enter') { + handleSearchSubmit(event); + } + }} + renderInput={(params) => ( + + )} + /> + + + + ); + } + + function FilterForm(props: any) { + // IMPORT + return ( + + + + + + ); + } + + function createData(doctor: Practitioner): Practitioner { + return { + ...doctor, + }; + } + + function CheckStatus(props: { row: ReturnType }) { + const { row } = props; + if (row.active === 1) { + return ( + + ); + } else { + return ( + + ); + } + } + + function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(false); + const [openDialog, setOpenDialog] = React.useState(false); + + const handleDelete = (model: any) => { + axios + .delete(`/doctors/${row.id}`) + .then((res) => { + setDataTableData({ + ...dataTableData, + data: dataTableData.data.filter((model) => model.id != row.id), + }); + enqueueSnackbar('Data berhasil dihapus', { variant: 'success' }); + }) + .catch((error) => { + enqueueSnackbar( + error.response.data.message ?? error.message ?? 'Failed Processing Request', + { variant: 'error' } + ); + }); + }; + + return ( + + + + setOpen(!open)}> + {open ? : } + + + {row.name ? row.name : '-'} + + + {row.organizations?.map((org) => org.organization_name).join(', ') ?? '-'} + + + {row.specialties ? row.specialties.map((spec) => spec.specialty_name).join(', ') : '-'} + + {row.address ? row.address : '-'} + {/* + + */} + + {/* + + + + + + */} + + {/* COLLAPSIBLE ROW */} + + + + + + + + + Pendidikan + + + : {row.education ? row.education : '-'} + + + + Pengalaman Kerja + + + : {row.experience ? row.experience : '-'} + + + + Jenis Kelamin + + + : {row.gender ? row.gender : '-'} + + + + + + + Email + + + : {row.email ? row.email : '-'} + + + + No. Telp + + + : {row.phone ? row.phone : '-'} + + + + Penghargaan + + + : {row.award ? row.award : '-'} + + + + + + + + + + {/* END COLLAPSIBLE ROW */} + + { + setOpenDialog(false); + }} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + + + + Apakah anda yakin ingin menghapus + + + {row.name}? + + + + + + + + + ); + } + + const headStyle = { + fontWeight: 'bold', + }; + // 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('/doctors', { + params: filter, + }); + setDataTableLoading(false); + setDataTableData(response.data); + }; + + // const applyFilter = async (searchFilter: string) => { + // await loadDataTableData({ search: searchFilter }); + // setSearchParams({ search: searchFilter }); + // }; + + const applyItems = async ( + searchFilter: string, + searchFilterOrganization: string, + searchFilterSpecialities: string + ) => { + await loadDataTableData({ + search: searchFilter, + organization_id: searchFilterOrganization, + speciality_id: searchFilterSpecialities, + }); + setSearchParamsFilter({ + search: searchFilter, + organization_id: searchFilterOrganization, + speciality_id: searchFilterSpecialities, + }); + }; + + const handlePageChange = (event: ChangeEvent, value: number) => { + const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); + loadDataTableData(filter); + setSearchParams(filter); + }; + + useEffect(() => { + loadDataTableData(); + }, []); + + return ( + + {/* */} + + + + + {/* The Main Table */} + + + + + + + Nama Dokter + + + Rumah Sakit + + + Spesialis + + + Alamat + + {/* + Aksi + */} + + + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length == 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row) => ( + + ))} + + )} +
+
+ + +
+
+ ); +} diff --git a/frontend/dashboard/src/pages/Master/Hospitals/Create.tsx b/frontend/dashboard/src/pages/Master/Hospitals/Create.tsx new file mode 100644 index 00000000..03cc498e --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Hospitals/Create.tsx @@ -0,0 +1,56 @@ +import { useEffect, useState } from 'react'; +import { paramCase } from 'change-case'; +import { useParams, useLocation } from 'react-router-dom'; +// @mui +import { Container, Stack } from '@mui/material'; +import useSettings from '../../../hooks/useSettings'; +import Page from '../../../components/Page'; +import Form from './Form'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import axios from '../../../utils/axios'; +import { Organizations } from '../../../@types/organization'; +import ButtonBack from '../../../components/ButtonBack'; + +export default function Create() { + const { themeStretch } = useSettings(); + const { id } = useParams(); + + const isEdit = id ? true : false; + + const [currentOrganizations, setCurrentOrganizations] = useState(); + + useEffect(() => { + if (isEdit) { + axios.get('/organizations/' + id + '/edit').then((res) => { + setCurrentOrganizations(res.data); + }); + } + }, [id]); + + return ( + + + + + + + +
+ + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Hospitals/Form.tsx b/frontend/dashboard/src/pages/Master/Hospitals/Form.tsx new file mode 100644 index 00000000..546d225a --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Hospitals/Form.tsx @@ -0,0 +1,778 @@ +import * as Yup from 'yup'; +import { useSnackbar } from 'notistack'; +import { useNavigate } from 'react-router-dom'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import MenuItem from '@mui/material/MenuItem'; + +import Select, { SelectChangeEvent } from '@mui/material/Select'; +import * as React from 'react'; + +// form +import { Controller, useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +// @mui +import { styled } from '@mui/material/styles'; +import { LoadingButton } from '@mui/lab'; +import { + Box, + Button, + ButtonGroup, + Card, + Grid, + Stack, + Typography, + TextField, + Tab, + Chip, + Autocomplete, +} from '@mui/material'; + +import TabContext from '@mui/lab/TabContext'; +import TabList from '@mui/lab/TabList'; +import TabPanel from '@mui/lab/TabPanel'; + +import CancelIcon from '@mui/icons-material/Cancel'; + +// components +import { + FormProvider, + RHFTextField, + RHFRadioGroup, + RHFUploadAvatar, + RHFSwitch, + RHFSelect, + RHFEditor, + RHFDatepicker, + RHFMultiCheckbox, + RHFCheckbox, + RHFCustomMultiCheckbox, +} from '../../../components/hook-form'; +import axios from '../../../utils/axios'; +import { fCurrency } from '../../../utils/formatNumber'; +import { Organizations } from '../../../@types/organization'; +import { Provinces } from '../../../@types/organization'; +import { Cities } from '../../../@types/organization'; +import { Districts } from '../../../@types/organization'; +import { Villages } from '../../../@types/organization'; + +import { Label } from '@mui/icons-material'; +import MyDropzone from '../../../components/MyDropzone'; + +const LabelStyle = styled(Typography)(({ theme }) => ({ + ...theme.typography.h6, + marginBottom: theme.spacing(2), + marginTop: theme.spacing(2), +})); + +const HeaderStyle = styled('header')(({ theme }) => ({ + padding: theme.spacing(5), + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', +})); + +const Title = styled(Typography)(({ theme }) => ({ + ...theme.typography.h4, + boxShadow: 'none', + // paddingBottom: theme.spacing(3), + fontWeight: 700, + color: '#005B7F', +})); + +// const [timezone, setTimezone] = React.useState(''); + +// const selectInput = (event: SelectChangeEvent) => { +// setTimezone(event.target.value as string); +// const name = event.target.name; +// const value = event.target.value; +// setInputs((values) => ({ ...values, [name]: value })); +// }; +// const [inputs, setInputs] = useState({}); + +interface FormValuesProps extends Partial { + taxes: boolean; + inStock: boolean; +} + +type Props = { + isEdit: boolean; + currentOrganizations?: Organizations; +}; + +export default function OrganizationsForm({ isEdit, currentOrganizations }: Props) { + const navigate = useNavigate(); + const [organizations_group, setOrganizationsGroups] = useState([]); + + // const [ errors, setErrors ] = useState<{ [key: string]: string }>({}); + + const { enqueueSnackbar } = useSnackbar(); + + const NewCorporateSchema = Yup.object().shape({ + name: Yup.string().required('Name is required'), + code: Yup.string().required('Corporate Code is required'), + active: Yup.boolean().required('Corporate Status is required'), + lat: Yup.string().required('Latitude is required'), + lng: Yup.string().required('Longitude is required'), + timezone: Yup.string().required('Timezone is required'), + // file: Yup.boolean().required('Corporate Status is required'), + }); + + const defaultValues = useMemo( + () => ({ + name: currentOrganizations?.name || '', + code: currentOrganizations?.code || '', + phone: currentOrganizations?.phone || '', + lat: currentOrganizations?.lat || '', + lng: currentOrganizations?.lng || '', + address: currentOrganizations?.address || '', + timezone: currentOrganizations?.timezone || '', + active: currentOrganizations?.active === 1 ? true : false, + province_id: currentOrganizations?.province_id || '', + city_id: currentOrganizations?.city_id || '', + district_id: currentOrganizations?.district_id || '', + village_id: currentOrganizations?.village_id || '', + postal_code: currentOrganizations?.postal_code || '', + description: currentOrganizations?.description || '', + technology: currentOrganizations?.technology || '', + support_services: currentOrganizations?.support_services || '', + merchant_code: currentOrganizations?.merchant_code || '', + merchant_key: currentOrganizations?.merchant_key || '', + image_url: currentOrganizations?.image_url || '', + region_groups: currentOrganizations?.region_groups || '', + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [currentOrganizations] + ); + + console.log('defaultValues', defaultValues); + + function StatusLabel({ value }: { value: boolean }) { + return ( + + ); + } + + const methods = useForm({ + resolver: yupResolver(NewCorporateSchema), + defaultValues, + }); + + const { + reset, + watch, + control, + setValue, + getValues, + setError, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + const values = watch(); + + useEffect(() => { + if (isEdit && currentOrganizations) { + reset(defaultValues); + } + if (!isEdit) { + reset(defaultValues); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isEdit, currentOrganizations]); + + const currentImage = currentOrganizations?.image_url; + console.log('currentImage', currentImage); + + console.log('current_image', currentImage); + + const [file, setFile] = useState(null); + console.log('file', file); + + const onSubmit = async (data: FormValuesProps) => { + try { + const formData = new FormData(); + console.log('data', data); + formData.append('name', data.name); + formData.append('code', data.code); + formData.append('phone', data.phone); + formData.append('lat', data.lat); + formData.append('lng', data.lng); + formData.append('address', data.address); + formData.append('timezone', data.timezone); + formData.append('active', data.active ? '1' : '0'); + if (data.province_id === currentOrganizations?.province_id) { + formData.append('province_id', data.province_id); + } else { + formData.append('province_id', data.province_id?.value ?? ''); + } + + if (data.city_id === currentOrganizations?.city_id) { + formData.append('city_id', data.city_id); + } else { + formData.append('city_id', data.city_id?.value ?? ''); + } + + if (data.district_id === currentOrganizations?.district_id) { + formData.append('district_id', data.district_id); + } else { + formData.append('district_id', data.district_id?.value ?? ''); + } + + if (data.village_id === currentOrganizations?.village_id) { + formData.append('village_id', data.village_id); + } else { + formData.append('village_id', data.village_id?.value ?? ''); + } + if (data.region_groups === currentOrganizations?.region_groups) { + formData.append('region_groups', data.region_groups); + } else { + formData.append('region_groups', data.region_groups?.value ?? ''); + } + + formData.append('postal_code', data.postal_code); + formData.append('description', data.description); + formData.append('technology', data.technology); + formData.append('support_services', data.support_services); + formData.append('merchant_code', data.merchant_code); + formData.append('merchant_key', data.merchant_key); + formData.append('image', file); + + if (!isEdit) { + const response = await axios.post('/organizations', formData); + } else { + formData.append('_method', 'PUT'); + const response = await axios.post( + '/organizations/' + currentOrganizations?.id ?? '', + formData + ); + } + reset(); + enqueueSnackbar( + !isEdit ? 'Organizations Created Successfully!' : 'Organizations Udpated Successfully!', + { variant: 'success' } + ); + navigate('/master/organizations'); + } catch (error: any) { + if (error && error.response.status === 422) { + for (const [key, value] of Object.entries(error.response.data.errors)) { + setError(key, { message: value[0] }); + enqueueSnackbar(value[0] ?? 'Failed Processing Request', { variant: 'error' }); + } + } else { + enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' }); + } + } + + const ascent = document?.querySelector('ascent'); + if (ascent != null) { + ascent.innerHTML = ''; + } + }; + + const [valueTab, setValueTab] = React.useState('1'); + + const handleChangeTab = (event: React.SyntheticEvent, newValueTab: string) => { + setValueTab(newValueTab); + }; + + const handleDrop = useCallback( + (acceptedFiles) => { + setValue( + 'logo', + acceptedFiles.map((file: Blob | MediaSource) => + Object.assign(file, { + preview: URL.createObjectURL(file), + }) + ) + ); + }, + [setValue] + ); + + const handleRemove = (file: File | string) => { + setValue('logo', null); + }; + + const [province, setProvince] = useState([]); + const [city, setCity] = useState([]); + const [district, setDistrict] = useState([]); + // const [village, setVillage] = useState([]); + + useEffect(() => { + axios.get('/province').then((res) => { + setProvince(res.data.data.map((item: any) => ({ value: item.id, label: item.name }))); + }); + + const loadCity = async () => { + if (values.province_id == currentOrganizations?.province_id) { + const res = await axios.get('/city?province_id=' + values.province_id); + setCity(res.data.data.map((item: any) => ({ value: item.id, label: item.name }))); + } else { + const res = await axios.get('/city?province_id=' + values.province_id?.value); + setCity(res.data.data.map((item: any) => ({ value: item.id, label: item.name }))); + } + }; + + const loadDistrict = async () => { + if (values.city_id == currentOrganizations?.city_id) { + const res = await axios.get('/district?city_id=' + values.city_id); + setDistrict(res.data.data.map((item: any) => ({ value: item.id, label: item.name }))); + } else { + const res = await axios.get('/district?city_id=' + values.city_id?.value); + setDistrict(res.data.data.map((item: any) => ({ value: item.id, label: item.name }))); + } + }; + + // if (values.province_id) { + // if (values.city_id) { + // loadDistrict(); + // } else { + // loadCity(); + // } + // } else { + // axios.get('/province').then((res) => { + // setProvince(res.data.data.map((item: any) => ({ value: item.id, label: item.name }))); + // }); + // } + + if (values.province_id) { + loadCity(); + } + + if (values.city_id) { + loadDistrict(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [values.province_id, values.city_id, values.district_id]); + + console.log('province', values.province_id); + console.log('city', values.city_id); + console.log('district', values.district_id); + + const findValueProvince = province.find( + (item: any) => item.value === currentOrganizations?.province_id + ); + const findValueCity = city.find((item: any) => item.value === currentOrganizations?.city_id); + const findValueDistrict = district.find( + (item: any) => item.value === currentOrganizations?.district_id + ); + + console.log('findValueProvince', findValueProvince); + console.log('findValueCity', findValueCity); + console.log('findValueDistrict', findValueDistrict); + const timezone = [ + { + value: 'WIB', + label: 'WIB', + }, + { + value: 'WITA', + label: 'WITA', + }, + { + value: 'WIT', + label: 'WIT', + }, + ]; + const region_groups = [ + { + value: 'Jabodetabek', + label: 'Jabodetabek', + }, + { + value: 'Jawa', + label: 'Jawa', + }, + { + value: 'Kalimantan', + label: 'Kalimantan', + }, + { + value: 'Papua', + label: 'Papua', + }, + { + value: 'Sulawesi', + label: 'Sulawesi', + }, + { + value: 'Sumatera', + label: 'Sumatera', + }, + ]; + + const findVaalueGroupWilayah = region_groups.find( + (item: any) => item.value === currentOrganizations?.region_groups + ); + + return ( + + + + + + Data Rumah Sakit + + + {/* Status Rumah Sakit */} + + + + + + + + + + + + + + + + + + Nama Rumah Sakit + + + + Pilih Foto Rumah Sakit + + + + + + Nomor IGD + + + + Code Rumah Sakit + + + + Group Wilayah + ( + + option.label ?? findVaalueGroupWilayah?.label ?? '' + } + value={value} + onChange={(event: any, newValue: any) => { + console.log('newValue', newValue); + setValue('region_groups', newValue?.value); + onChange(newValue); + }} + renderInput={(params) => ( + + )} + /> + )} + /> + + + Alamat + + + + Provinsi + {/* + ( + + )} + /> */} + + ( + + option.label ?? findValueProvince?.label ?? '' + } + value={value} + onChange={(event: any, newValue: any) => { + console.log('newValue', newValue); + setValue('province_id', newValue?.value); + onChange(newValue); + }} + renderInput={(params) => ( + + )} + /> + )} + /> + + + Kabupaten / Kota + {/* ( + + )} + /> */} + ( + option.label ?? findValueCity?.label ?? ''} + value={value} + onChange={(event: any, newValue: any) => { + console.log('newValue', newValue); + setValue('city_id', newValue?.value); + onChange(newValue); + }} + renderInput={(params) => ( + + )} + /> + )} + /> + + + + Kecamatan + + {/* ( + + )} + /> */} + + ( + + option.label ?? findValueDistrict?.label ?? '' + } + value={value} + onChange={(event: any, newValue: any) => { + console.log('newValue', newValue); + setValue('district_id', newValue?.value); + onChange(newValue); + }} + renderInput={(params) => ( + + )} + /> + )} + /> + + + Kode Pos + + + + Latitude + + + + Longitude + + + + Timezone + {/* */} + + + ))} + + + + + + + + + + Deskripsi + + + + Teknologi + + + + Layanan Penunjang + + + + + + + + + + Merchant Code + + + + Merchant Key + + + + + + + + + + + + {!isEdit ? 'Simpan' : 'Simpan Perubahan'} + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Hospitals/Index.tsx b/frontend/dashboard/src/pages/Master/Hospitals/Index.tsx new file mode 100644 index 00000000..a085ad31 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Hospitals/Index.tsx @@ -0,0 +1,35 @@ +import { Card, Grid, Container } from '@mui/material'; +import { useParams } from 'react-router-dom'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import Page from '../../../components/Page'; +import useSettings from '../../../hooks/useSettings'; +import List from './List'; + +export default function Organizations() { + const { themeStretch } = useSettings(); + + const { id } = useParams(); + + const pageTitle = 'Rumah Sakit'; + return ( + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Hospitals/List.tsx b/frontend/dashboard/src/pages/Master/Hospitals/List.tsx new file mode 100644 index 00000000..eda78d2f --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Hospitals/List.tsx @@ -0,0 +1,485 @@ +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, + Chip, + styled, + DialogTitle, + Dialog, + DialogContent, + DialogContentText, + DialogActions, + InputAdornment, +} 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'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; + +import { + Link, + NavLink as RouterLink, + useSearchParams, + useNavigate, + useParams, +} from 'react-router-dom'; +// hooks +import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; +import useSettings from '../../../hooks/useSettings'; +// components +import axios from '../../../utils/axios'; +import { LaravelPaginatedData } from '../../../@types/paginated-data'; +import { Icd } from '../../../@types/diagnosis'; +import BasePagination from '../../../components/BasePagination'; +import { Organizations } from '../../../@types/organization'; +import CreateIcon from '@mui/icons-material/Create'; +import { Props } from '../../../components/editor/index'; +import { red } from '@mui/material/colors'; +import { margin, padding } from '@mui/system'; +import { enqueueSnackbar } from 'notistack'; +import SvgIconStyle from '../../../components/SvgIconStyle'; +// import ButtonCreate from '../../../components/ButtonCreate'; +import { Search } from '@mui/icons-material'; +import { Icon } from '@iconify/react'; + +// ---------------------------------------------------------------------- + +const getIcon = (name: string) => ( + +); + +const ICONS = { + show: getIcon('ic_show'), + edit: getIcon('ic_edit'), + delete: getIcon('ic_delete'), +}; + +export default function List() { + // Generate the every row of the table + + const navigate = useNavigate(); + const { organization_id } = useParams(); + const [searchParams, setSearchParams] = useSearchParams(); + const [importResult, setImportResult] = useState(null); + const Item = styled(Paper)(({ theme }) => ({ + textAlign: 'left', + })); + + function SearchInput(props: any) { + // SEARCH + const searchInput = useRef(null); + const [searchText, setSearchText] = 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') ?? ''); + }, [searchParams]); + + return ( + + + + + ), + placeholder: 'Search', + }} + /> + + ); + } + + function ImportForm(props: any) { + // IMPORT + // Create Button Menu + + return ( + + + + {/* + + */} + + ); + } + + function createData(organization: Organizations): Organizations { + return { + ...organization, + }; + } + + function CheckStatus(props: { row: ReturnType }) { + const { row } = props; + if (row.active === 1) { + return ( + + ); + } else { + return ( + + ); + } + } + + function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(false); + const [openDialog, setOpenDialog] = React.useState(false); + + const handleActivate = (model: any, status: string) => { + axios + .put(`/organizations/${row.id}/activation`, { + // service_code: service.service_code, + active: status == 'active', + }) + .then((res) => { + setDataTableData({ + ...dataTableData, + data: dataTableData.data.map((model) => { + let updatedModel = model; + if (row.id == model.id) { + updatedModel.active = res.data.data.active; + } + return updatedModel; + }), + }); + }) + .catch((error) => { + enqueueSnackbar( + error.response.data.message ?? error.message ?? 'Failed Processing Request', + { variant: 'error' } + ); + }); + }; + + const handleDelete = (model: any) => { + axios + .delete(`/organizations/${row.id}`) + .then((res) => { + setDataTableData({ + ...dataTableData, + data: dataTableData.data.filter((model) => model.id != row.id), + }); + enqueueSnackbar('Data berhasil dihapus', { variant: 'success' }); + }) + .catch((error) => { + enqueueSnackbar( + error.response.data.message ?? error.message ?? 'Failed Processing Request', + { variant: 'error' } + ); + }); + }; + + return ( + + *': { borderBottom: 'unset' }, + }} + > + + setOpen(!open)}> + {open ? : } + + + {row.name} + {row.phone} + {row.address?.text} + + {/* + + + + + + + + + */} + + + {/* COLLAPSIBLE ROW */} + + + + + + + + + Kode Rumah Sakit + + + : {row.code ? row.code : '-'} + + + + Longitude + + + : {row.lng ? row.lng : '-'} + + + + Latittude + + + : {row.lat ? row.lat : '-'} + + + + + + + + + + {/* END COLLAPSIBLE ROW */} + + { + setOpenDialog(false); + }} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + {/* {'Hapus Dokter'} */} + + + + Apakah anda yakin ingin menghapus + + + {row.name}? + + + + + + + + + ); + } + const headStyle = { + fontWeight: 'bold', + }; + // 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('/organizations', { + params: filter, + }); + console.log(response.data); + setDataTableLoading(false); + setDataTableData(response.data); + }; + + 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 */} + + + + + + + Rumah Sakit + + + Nomor IGD + + + Alamat + + {/* + Aksi + */} + + + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length == 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row) => ( + + ))} + + )} +
+
+ + +
+
+ ); +} diff --git a/frontend/dashboard/src/routes/index.tsx b/frontend/dashboard/src/routes/index.tsx index 6ae3e352..cbdecd1d 100755 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -68,8 +68,9 @@ export default function Router() { - ), - children:[ + + ), + children: [ { element: , index: true }, { path: 'dashboard', @@ -187,6 +188,14 @@ export default function Router() { element: , }, + { + path: 'master/doctors', + element: , + }, + { + path: 'master/hospitals', + element: , + }, { path: 'master/diagnosis', element: , @@ -206,31 +215,30 @@ export default function Router() { element: , }, - { path: 'claims', element: , }, { path: 'claims/create', - element: + element: , }, { path: 'claims/:id', - element: - } - ] + element: , + }, + ], }, // { // path: '/dashboard', // element: , // children: [ // { element: , index: true }, - // { path: 'one', element: + // { path: 'one', element: // }, - // { path: 'two', element: + // { path: 'two', element: // }, - // { path: 'three', element: + // { path: 'three', element: // }, // { // path: 'user', @@ -269,27 +277,39 @@ const CorporateCreate = Loadable(lazy(() => import('../pages/Corporates/CreateUp const CorporateShow = Loadable(lazy(() => import('../pages/Corporates/Show'))); const CorporateDivisions = Loadable(lazy(() => import('../pages/Corporates/Division/Index'))); -const CorporateDivisionsCreate = Loadable(lazy(() => import('../pages/Corporates/Division/CreateUpdate'))); +const CorporateDivisionsCreate = Loadable( + lazy(() => import('../pages/Corporates/Division/CreateUpdate')) +); const CorporateMembers = Loadable(lazy(() => import('../pages/Corporates/Member/Index'))); const BenefitCreate = Loadable(lazy(() => import('../pages/Corporates/Benefit/Create'))); const Benefits = Loadable(lazy(() => import('../pages/Corporates/Benefit/Index'))); -const CorporateBenefitsCreate = Loadable(lazy(() => import('../pages/Corporates/CorporateBenefit/CreateUpdate'))); -const CorporateBenefits = Loadable(lazy(() => import('../pages/Corporates/CorporateBenefit/Index'))); +const CorporateBenefitsCreate = Loadable( + lazy(() => import('../pages/Corporates/CorporateBenefit/CreateUpdate')) +); +const CorporateBenefits = Loadable( + lazy(() => import('../pages/Corporates/CorporateBenefit/Index')) +); -const CorporatePlanCreate = Loadable(lazy(() => import('../pages/Corporates/CorporatePlan/CreateUpdate'))); +const CorporatePlanCreate = Loadable( + lazy(() => import('../pages/Corporates/CorporatePlan/CreateUpdate')) +); const CorporatePlans = Loadable(lazy(() => import('../pages/Corporates/CorporatePlan/Index'))); const PlanCreate = Loadable(lazy(() => import('../pages/Corporates/Plan/Create'))); const Plans = Loadable(lazy(() => import('../pages/Corporates/Plan/Index'))); -const DiagnosisExclusions = Loadable(lazy(() => import('../pages/Corporates/DiagnosisExclusion/Index'))); +const DiagnosisExclusions = Loadable( + lazy(() => import('../pages/Corporates/DiagnosisExclusion/Index')) +); const CorporateFormularium = Loadable(lazy(() => import('../pages/Corporates/Formularium/Index'))); const MasterDiagnosis = Loadable(lazy(() => import('../pages/Master/Diagnosis/Index'))); +const MasterDoctors = Loadable(lazy(() => import('../pages/Master/Doctors/Index'))); +const MasterHospitals = Loadable(lazy(() => import('../pages/Master/Hospitals/Index'))); const MasterDrug = Loadable(lazy(() => import('../pages/Master/Drug/Index'))); @@ -300,7 +320,9 @@ const CorporateServices = Loadable(lazy(() => import('../pages/Corporates/Servic const CorporateServicesCreate = Loadable(lazy(() => import('../pages/Corporates/Services/Create'))); const CorporateHospitals = Loadable(lazy(() => import('../pages/Corporates/Hospital/Index'))); -const CorporateClaimHistories = Loadable(lazy(() => import('../pages/Corporates/ClaimHistory/Index'))); +const CorporateClaimHistories = Loadable( + lazy(() => import('../pages/Corporates/ClaimHistory/Index')) +); const Claims = Loadable(lazy(() => import('../pages/Claims/Index'))); -const ClaimsCreate = Loadable(lazy(() => import('../pages/Claims/CreateUpdate'))); \ No newline at end of file +const ClaimsCreate = Loadable(lazy(() => import('../pages/Claims/CreateUpdate')));