Merge pull request 'Menambahkan menu dokter dan tenaga medis, admin bisa create, edit, delet dokter' (#36) from nico/3-des-25 into staggingweb
Reviewed-on: http://wibugit.wibudev.com/wibu/desa-darmasaba/pulls/36
This commit is contained in:
@@ -793,14 +793,12 @@ model FasilitasKesehatan {
|
|||||||
informasiUmumId String
|
informasiUmumId String
|
||||||
layananunggulan LayananUnggulan @relation(fields: [layananUnggulanId], references: [id])
|
layananunggulan LayananUnggulan @relation(fields: [layananUnggulanId], references: [id])
|
||||||
layananUnggulanId String
|
layananUnggulanId String
|
||||||
dokterdantenagamedis DokterdanTenagaMedis @relation(fields: [dokterdanTenagaMedisId], references: [id])
|
dokterdantenagamedis DokterdanTenagaMedis[] @relation("Dokter")
|
||||||
dokterdanTenagaMedisId String
|
|
||||||
fasilitaspendukung FasilitasPendukung @relation(fields: [fasilitasPendukungId], references: [id])
|
fasilitaspendukung FasilitasPendukung @relation(fields: [fasilitasPendukungId], references: [id])
|
||||||
fasilitasPendukungId String
|
fasilitasPendukungId String
|
||||||
prosedurpendaftaran ProsedurPendaftaran @relation(fields: [prosedurPendaftaranId], references: [id])
|
prosedurpendaftaran ProsedurPendaftaran @relation(fields: [prosedurPendaftaranId], references: [id])
|
||||||
prosedurPendaftaranId String
|
prosedurPendaftaranId String
|
||||||
tarifdanlayanan TarifDanLayanan @relation(fields: [tarifDanLayananId], references: [id])
|
tarifdanlayanan TarifDanLayanan[] @relation("Tarif")
|
||||||
tarifDanLayananId String
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model InformasiUmum {
|
model InformasiUmum {
|
||||||
@@ -830,11 +828,16 @@ model DokterdanTenagaMedis {
|
|||||||
name String
|
name String
|
||||||
specialist String
|
specialist String
|
||||||
jadwal String
|
jadwal String
|
||||||
|
jadwalLibur String
|
||||||
|
jamBukaOperasional String
|
||||||
|
jamTutupOperasional String
|
||||||
|
jamBukaLibur String
|
||||||
|
jamTutupLibur String
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime @default(now())
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
FasilitasKesehatan FasilitasKesehatan[]
|
FasilitasKesehatan FasilitasKesehatan[] @relation("Dokter")
|
||||||
}
|
}
|
||||||
|
|
||||||
model FasilitasPendukung {
|
model FasilitasPendukung {
|
||||||
@@ -865,7 +868,7 @@ model TarifDanLayanan {
|
|||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime @default(now())
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
FasilitasKesehatan FasilitasKesehatan[]
|
FasilitasKesehatan FasilitasKesehatan[] @relation("Tarif")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================= JADWAL KEGIATAN ========================================= //
|
// ========================================= JADWAL KEGIATAN ========================================= //
|
||||||
|
|||||||
@@ -9,29 +9,30 @@ import { z } from "zod";
|
|||||||
// Validasi form
|
// Validasi form
|
||||||
const templateForm = z.object({
|
const templateForm = z.object({
|
||||||
name: z.string().min(1, "Nama harus diisi"),
|
name: z.string().min(1, "Nama harus diisi"),
|
||||||
|
|
||||||
informasiUmum: z.object({
|
informasiUmum: z.object({
|
||||||
fasilitas: z.string().min(1, "Fasilitas harus diisi"),
|
fasilitas: z.string().min(1),
|
||||||
alamat: z.string().min(1, "Alamat harus diisi"),
|
alamat: z.string().min(1),
|
||||||
jamOperasional: z.string().min(1, "Jam operasional harus diisi"),
|
jamOperasional: z.string().min(1),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
layananUnggulan: z.object({
|
layananUnggulan: z.object({
|
||||||
content: z.string().min(1, "Layanan unggulan harus diisi"),
|
content: z.string().min(1),
|
||||||
}),
|
|
||||||
dokterdanTenagaMedis: z.object({
|
|
||||||
name: z.string().min(1, "Nama dokter harus diisi"),
|
|
||||||
specialist: z.string().min(1, "Spesialis harus diisi"),
|
|
||||||
jadwal: z.string().min(1, "Jadwal harus diisi"),
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// NOW ARRAY OF STRING (ID)
|
||||||
|
dokterdanTenagaMedis: z.array(z.string()).min(1, "Minimal pilih 1 dokter"),
|
||||||
|
|
||||||
fasilitasPendukung: z.object({
|
fasilitasPendukung: z.object({
|
||||||
content: z.string().min(1, "Fasilitas pendukung harus diisi"),
|
content: z.string().min(1),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
prosedurPendaftaran: z.object({
|
prosedurPendaftaran: z.object({
|
||||||
content: z.string().min(1, "Prosedur pendaftaran harus diisi"),
|
content: z.string().min(1),
|
||||||
}),
|
|
||||||
tarifDanLayanan: z.object({
|
|
||||||
layanan: z.string().min(1, "Layanan harus diisi"),
|
|
||||||
tarif: z.string().min(1, "Tarif harus diisi"),
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// NOW ARRAY OF STRING (ID)
|
||||||
|
tarifDanLayanan: z.array(z.string()).min(1, "Minimal pilih 1 tarif"),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Default form kosong
|
// Default form kosong
|
||||||
@@ -45,21 +46,34 @@ const defaultForm = {
|
|||||||
layananUnggulan: {
|
layananUnggulan: {
|
||||||
content: "",
|
content: "",
|
||||||
},
|
},
|
||||||
dokterdanTenagaMedis: {
|
|
||||||
name: "",
|
dokterdanTenagaMedis: [] as string[], // ← array kosong
|
||||||
specialist: "",
|
tarifDanLayanan: [] as string[], // ← array kosong
|
||||||
jadwal: "",
|
|
||||||
},
|
|
||||||
fasilitasPendukung: {
|
fasilitasPendukung: {
|
||||||
content: "",
|
content: "",
|
||||||
},
|
},
|
||||||
prosedurPendaftaran: {
|
prosedurPendaftaran: {
|
||||||
content: "",
|
content: "",
|
||||||
},
|
},
|
||||||
tarifDanLayanan: {
|
};
|
||||||
layanan: "",
|
|
||||||
tarif: "",
|
type DokterItem = {
|
||||||
},
|
id: string;
|
||||||
|
name: string;
|
||||||
|
specialist: string;
|
||||||
|
jadwal: string;
|
||||||
|
jadwalLibur: string;
|
||||||
|
jamBukaOperasional: string;
|
||||||
|
jamTutupOperasional: string;
|
||||||
|
jamBukaLibur: string;
|
||||||
|
jamTutupLibur: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TarifItem = {
|
||||||
|
id: string;
|
||||||
|
layanan: string;
|
||||||
|
tarif: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fasilitasKesehatan = proxy({
|
const fasilitasKesehatan = proxy({
|
||||||
@@ -186,33 +200,26 @@ const fasilitasKesehatan = proxy({
|
|||||||
|
|
||||||
const result = await res.json();
|
const result = await res.json();
|
||||||
const data = result.data;
|
const data = result.data;
|
||||||
|
this.id = data.id;
|
||||||
fasilitasKesehatan.edit.id = data.id;
|
this.form = {
|
||||||
fasilitasKesehatan.edit.form = {
|
|
||||||
name: data.name,
|
name: data.name,
|
||||||
informasiUmum: {
|
informasiUmum: {
|
||||||
fasilitas: data.informasiumum.fasilitas,
|
fasilitas: data.informasiumum.fasilitas,
|
||||||
alamat: data.informasiumum.alamat,
|
alamat: data.informasiumum.alamat,
|
||||||
jamOperasional: data.informasiumum.jamOperasional,
|
jamOperasional: data.informasiumum.jamOperasional,
|
||||||
},
|
},
|
||||||
layananUnggulan: {
|
|
||||||
content: data.layananunggulan.content,
|
|
||||||
},
|
|
||||||
dokterdanTenagaMedis: {
|
|
||||||
name: data.dokterdantenagamedis.name,
|
|
||||||
specialist: data.dokterdantenagamedis.specialist,
|
|
||||||
jadwal: data.dokterdantenagamedis.jadwal,
|
|
||||||
},
|
|
||||||
fasilitasPendukung: {
|
fasilitasPendukung: {
|
||||||
content: data.fasilitaspendukung.content,
|
content: data.fasilitaspendukung.content,
|
||||||
},
|
},
|
||||||
prosedurPendaftaran: {
|
prosedurPendaftaran: {
|
||||||
content: data.prosedurpendaftaran.content,
|
content: data.prosedurpendaftaran.content,
|
||||||
},
|
},
|
||||||
tarifDanLayanan: {
|
// map relasi -> array of IDs
|
||||||
layanan: data.tarifdanlayanan.layanan,
|
layananUnggulan: {
|
||||||
tarif: data.tarifdanlayanan.tarif,
|
content: data.layananunggulan.content,
|
||||||
},
|
},
|
||||||
|
dokterdanTenagaMedis: data.dokterdantenagamedis?.map((v: DokterItem) => v.id) ?? [],
|
||||||
|
tarifDanLayanan: data.tarifdanlayanan?.map((v: TarifItem) => v.id) ?? [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async submit() {
|
async submit() {
|
||||||
@@ -238,22 +245,15 @@ const fasilitasKesehatan = proxy({
|
|||||||
layananUnggulan: {
|
layananUnggulan: {
|
||||||
content: fasilitasKesehatan.edit.form.layananUnggulan.content,
|
content: fasilitasKesehatan.edit.form.layananUnggulan.content,
|
||||||
},
|
},
|
||||||
dokterdanTenagaMedis: {
|
dokterdanTenagaMedis:
|
||||||
name: fasilitasKesehatan.edit.form.dokterdanTenagaMedis.name,
|
fasilitasKesehatan.edit.form.dokterdanTenagaMedis,
|
||||||
specialist:
|
|
||||||
fasilitasKesehatan.edit.form.dokterdanTenagaMedis.specialist,
|
|
||||||
jadwal: fasilitasKesehatan.edit.form.dokterdanTenagaMedis.jadwal,
|
|
||||||
},
|
|
||||||
fasilitasPendukung: {
|
fasilitasPendukung: {
|
||||||
content: fasilitasKesehatan.edit.form.fasilitasPendukung.content,
|
content: fasilitasKesehatan.edit.form.fasilitasPendukung.content,
|
||||||
},
|
},
|
||||||
prosedurPendaftaran: {
|
prosedurPendaftaran: {
|
||||||
content: fasilitasKesehatan.edit.form.prosedurPendaftaran.content,
|
content: fasilitasKesehatan.edit.form.prosedurPendaftaran.content,
|
||||||
},
|
},
|
||||||
tarifDanLayanan: {
|
tarifDanLayanan: fasilitasKesehatan.edit.form.tarifDanLayanan,
|
||||||
layanan: fasilitasKesehatan.edit.form.tarifDanLayanan.layanan,
|
|
||||||
tarif: fasilitasKesehatan.edit.form.tarifDanLayanan.tarif,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
@@ -320,12 +320,26 @@ const templateDokterForm = z.object({
|
|||||||
name: z.string().min(1, "Nama tidak boleh kosong"),
|
name: z.string().min(1, "Nama tidak boleh kosong"),
|
||||||
specialist: z.string().min(1, "Spesialis tidak boleh kosong"),
|
specialist: z.string().min(1, "Spesialis tidak boleh kosong"),
|
||||||
jadwal: z.string().min(1, "Jadwal tidak boleh kosong"),
|
jadwal: z.string().min(1, "Jadwal tidak boleh kosong"),
|
||||||
|
jadwalLibur: z.string().min(1, "Jadwal libur tidak boleh kosong"),
|
||||||
|
jamBukaOperasional: z
|
||||||
|
.string()
|
||||||
|
.min(1, "Jam buka operasional tidak boleh kosong"),
|
||||||
|
jamTutupOperasional: z
|
||||||
|
.string()
|
||||||
|
.min(1, "Jam tutup operasional tidak boleh kosong"),
|
||||||
|
jamBukaLibur: z.string().min(1, "Jam buka libur tidak boleh kosong"),
|
||||||
|
jamTutupLibur: z.string().min(1, "Jam tutup libur tidak boleh kosong"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultDokterForm = {
|
const defaultDokterForm = {
|
||||||
name: "",
|
name: "",
|
||||||
specialist: "",
|
specialist: "",
|
||||||
jadwal: "",
|
jadwal: "",
|
||||||
|
jadwalLibur: "",
|
||||||
|
jamBukaOperasional: "",
|
||||||
|
jamTutupOperasional: "",
|
||||||
|
jamBukaLibur: "",
|
||||||
|
jamTutupLibur: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const dokter = proxy({
|
const dokter = proxy({
|
||||||
@@ -463,6 +477,11 @@ const dokter = proxy({
|
|||||||
name: data.name,
|
name: data.name,
|
||||||
specialist: data.specialist,
|
specialist: data.specialist,
|
||||||
jadwal: data.jadwal,
|
jadwal: data.jadwal,
|
||||||
|
jadwalLibur: data.jadwalLibur,
|
||||||
|
jamBukaOperasional: data.jamBukaOperasional,
|
||||||
|
jamTutupOperasional: data.jamTutupOperasional,
|
||||||
|
jamBukaLibur: data.jamBukaLibur,
|
||||||
|
jamTutupLibur: data.jamTutupLibur,
|
||||||
};
|
};
|
||||||
return data; // Return the loaded data
|
return data; // Return the loaded data
|
||||||
} else {
|
} else {
|
||||||
@@ -487,6 +506,11 @@ const dokter = proxy({
|
|||||||
name: this.form.name,
|
name: this.form.name,
|
||||||
specialist: this.form.specialist,
|
specialist: this.form.specialist,
|
||||||
jadwal: this.form.jadwal,
|
jadwal: this.form.jadwal,
|
||||||
|
jadwalLibur: this.form.jadwalLibur,
|
||||||
|
jamBukaOperasional: this.form.jamBukaOperasional,
|
||||||
|
jamTutupOperasional: this.form.jamTutupOperasional,
|
||||||
|
jamBukaLibur: this.form.jamBukaLibur,
|
||||||
|
jamTutupLibur: this.form.jamTutupLibur,
|
||||||
};
|
};
|
||||||
|
|
||||||
const cek = templateDokterForm.safeParse(formData);
|
const cek = templateDokterForm.safeParse(formData);
|
||||||
@@ -567,9 +591,255 @@ const dokter = proxy({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const templateTarifForm = z.object({
|
||||||
|
tarif: z.string().min(1, "Tarif tidak boleh kosong"),
|
||||||
|
layanan: z.string().min(1, "Layanan tidak boleh kosong"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultTarifForm = {
|
||||||
|
tarif: "",
|
||||||
|
layanan: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const tarif = proxy({
|
||||||
|
create: {
|
||||||
|
form: defaultTarifForm,
|
||||||
|
loading: false,
|
||||||
|
async create() {
|
||||||
|
const cek = templateTarifForm.safeParse(tarif.create.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
toast.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
tarif.create.loading = true;
|
||||||
|
const res = await ApiFetch.api.kesehatan.tarifdanlayanan["create"].post(
|
||||||
|
tarif.create.form
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
const id = res.data?.data;
|
||||||
|
if (id) {
|
||||||
|
toast.success("Sukses menambahkan");
|
||||||
|
tarif.create.form = { ...defaultTarifForm };
|
||||||
|
tarif.findMany.load();
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toast.error("failed create");
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.log((error as Error).message);
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
tarif.create.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findMany: {
|
||||||
|
data: null as
|
||||||
|
| Prisma.TarifDanLayananGetPayload<{
|
||||||
|
omit: {
|
||||||
|
isActive: true;
|
||||||
|
};
|
||||||
|
}>[]
|
||||||
|
| null,
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
|
loading: false,
|
||||||
|
search: "",
|
||||||
|
load: async (page = 1, limit = 10, search = "") => {
|
||||||
|
tarif.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
|
tarif.findMany.page = page;
|
||||||
|
tarif.findMany.search = search;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const query: any = { page, limit };
|
||||||
|
if (search) query.search = search;
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.kesehatan.tarifdanlayanan[
|
||||||
|
"findMany"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
|
if (res.status === 200 && res.data?.success) {
|
||||||
|
tarif.findMany.data = res.data.data ?? [];
|
||||||
|
tarif.findMany.totalPages = res.data.totalPages ?? 1;
|
||||||
|
} else {
|
||||||
|
tarif.findMany.data = [];
|
||||||
|
tarif.findMany.totalPages = 1;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal fetch tarif dan layanan paginated:", err);
|
||||||
|
tarif.findMany.data = [];
|
||||||
|
tarif.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
tarif.findMany.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as Prisma.TarifDanLayananGetPayload<{
|
||||||
|
omit: { isActive: true };
|
||||||
|
}> | null,
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/kesehatan/tarifdanlayanan/${id}`);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
tarif.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"Failed to fetch tarif dan layanan",
|
||||||
|
res.statusText
|
||||||
|
);
|
||||||
|
tarif.findUnique.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching tarif dan layanan", error);
|
||||||
|
tarif.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
id: "",
|
||||||
|
form: { ...defaultTarifForm },
|
||||||
|
loading: false,
|
||||||
|
async load(id: string) {
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/kesehatan/tarifdanlayanan/${id}`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result?.success) {
|
||||||
|
const data = result.data;
|
||||||
|
this.id = data.id;
|
||||||
|
this.form = {
|
||||||
|
tarif: data.tarif,
|
||||||
|
layanan: data.layanan
|
||||||
|
};
|
||||||
|
return data; // Return the loaded data
|
||||||
|
} else {
|
||||||
|
throw new Error(result?.message || "Gagal memuat data");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading tarif dan layanan:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error ? error.message : "Gagal memuat data"
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async submit() {
|
||||||
|
const id = this.id;
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = {
|
||||||
|
tarif: this.form.tarif,
|
||||||
|
layanan: this.form.layanan
|
||||||
|
};
|
||||||
|
|
||||||
|
const cek = templateTarifForm.safeParse(formData);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v: any) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
toast.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.loading = true;
|
||||||
|
const res = await fetch(`/api/kesehatan/tarifdanlayanan/${id}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(formData),
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await res.json();
|
||||||
|
|
||||||
|
if (!res.ok || !result?.success) {
|
||||||
|
throw new Error(result?.message || "Gagal update data");
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.success("Berhasil update data!");
|
||||||
|
await tarif.findMany.load();
|
||||||
|
return result.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Update error:", error);
|
||||||
|
toast.error("Gagal update data tarif dan layanan");
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) {
|
||||||
|
return toast.warn("ID tidak valid");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
tarif.delete.loading = true;
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/kesehatan/tarifdanlayanan/del/${id}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result?.success) {
|
||||||
|
toast.success(
|
||||||
|
result.message || "tarif dan layanan berhasil dihapus"
|
||||||
|
);
|
||||||
|
await tarif.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(
|
||||||
|
result?.message || "Gagal menghapus tarif dan layanan"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus tarif dan layanan");
|
||||||
|
} finally {
|
||||||
|
tarif.delete.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const fasilitasKesehatanState = proxy({
|
const fasilitasKesehatanState = proxy({
|
||||||
fasilitasKesehatan,
|
fasilitasKesehatan,
|
||||||
dokter,
|
dokter,
|
||||||
|
tarif
|
||||||
});
|
});
|
||||||
|
|
||||||
export default fasilitasKesehatanState;
|
export default fasilitasKesehatanState;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||||
@@ -10,19 +8,22 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Group,
|
Group,
|
||||||
Loader,
|
Loader,
|
||||||
|
MultiSelect,
|
||||||
Paper,
|
Paper,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Title
|
Title,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconArrowBack } from '@tabler/icons-react';
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
import { useParams, useRouter } from 'next/navigation';
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
interface FasilitasKesehatanFormBase {
|
// Tipe form yang SESUAI dengan logika relasi (array ID)
|
||||||
|
interface EditFasilitasKesehatanForm {
|
||||||
name: string;
|
name: string;
|
||||||
informasiUmum: {
|
informasiUmum: {
|
||||||
fasilitas: string;
|
fasilitas: string;
|
||||||
@@ -30,128 +31,92 @@ interface FasilitasKesehatanFormBase {
|
|||||||
jamOperasional: string;
|
jamOperasional: string;
|
||||||
};
|
};
|
||||||
layananUnggulan: { content: string };
|
layananUnggulan: { content: string };
|
||||||
dokterdanTenagaMedis: {
|
dokterdanTenagaMedis: string[]; // ← ARRAY ID
|
||||||
name: string;
|
|
||||||
specialist: string;
|
|
||||||
jadwal: string;
|
|
||||||
};
|
|
||||||
fasilitasPendukung: { content: string };
|
fasilitasPendukung: { content: string };
|
||||||
prosedurPendaftaran: { content: string };
|
prosedurPendaftaran: { content: string };
|
||||||
tarifDanLayanan: {
|
tarifDanLayanan: string[]; // ← ARRAY ID
|
||||||
layanan: string;
|
|
||||||
tarif: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditFasilitasKesehatan() {
|
function EditFasilitasKesehatan() {
|
||||||
const state = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
|
const state = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
|
||||||
|
const dokterState = useProxy(fasilitasKesehatanState.dokter);
|
||||||
|
const tarifState = useProxy(fasilitasKesehatanState.tarif);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams();
|
const params = useParams<{ id: string }>();
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
const [formData, setFormData] = useState<FasilitasKesehatanFormBase>({
|
const [formData, setFormData] = useState<EditFasilitasKesehatanForm>({
|
||||||
name: '',
|
name: '',
|
||||||
informasiUmum: { fasilitas: '', alamat: '', jamOperasional: '' },
|
informasiUmum: { fasilitas: '', alamat: '', jamOperasional: '' },
|
||||||
layananUnggulan: { content: '' },
|
layananUnggulan: { content: '' },
|
||||||
dokterdanTenagaMedis: { name: '', specialist: '', jadwal: '' },
|
dokterdanTenagaMedis: [],
|
||||||
fasilitasPendukung: { content: '' },
|
fasilitasPendukung: { content: '' },
|
||||||
prosedurPendaftaran: { content: '' },
|
prosedurPendaftaran: { content: '' },
|
||||||
tarifDanLayanan: { layanan: '', tarif: '' },
|
tarifDanLayanan: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const [originalData, setOriginalData] = useState<FasilitasKesehatanFormBase>({
|
// Load data fasilitas & daftar dokter/tarif
|
||||||
name: '',
|
useShallowEffect(() => {
|
||||||
informasiUmum: { fasilitas: '', alamat: '', jamOperasional: '' },
|
const loadAll = async () => {
|
||||||
layananUnggulan: { content: '' },
|
const id = params?.id;
|
||||||
dokterdanTenagaMedis: { name: '', specialist: '', jadwal: '' },
|
|
||||||
fasilitasPendukung: { content: '' },
|
|
||||||
prosedurPendaftaran: { content: '' },
|
|
||||||
tarifDanLayanan: { layanan: '', tarif: '' },
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper untuk update nested state
|
|
||||||
const updateForm = <K extends keyof FasilitasKesehatanFormBase>(
|
|
||||||
key: K,
|
|
||||||
value: FasilitasKesehatanFormBase[K]
|
|
||||||
) => setFormData(prev => ({ ...prev, [key]: value }));
|
|
||||||
|
|
||||||
const updateNested = <
|
|
||||||
K extends keyof FasilitasKesehatanFormBase,
|
|
||||||
N extends keyof FasilitasKesehatanFormBase[K]
|
|
||||||
>(key: K, nestedKey: N, value: FasilitasKesehatanFormBase[K][N]) =>
|
|
||||||
setFormData(prev => ({
|
|
||||||
...prev,
|
|
||||||
[key]: { ...prev[key] as object, [nestedKey]: value },
|
|
||||||
}));
|
|
||||||
|
|
||||||
const deepClone = (obj: any): any => {
|
|
||||||
try {
|
|
||||||
return JSON.parse(JSON.stringify(obj));
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Gagal deep clone dengan JSON fallback:', error);
|
|
||||||
return obj; // fallback (berisiko shared reference)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load data
|
|
||||||
useEffect(() => {
|
|
||||||
const load = async () => {
|
|
||||||
const id = params?.id as string;
|
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
|
|
||||||
try {
|
// Load dokter & tarif (untuk opsi MultiSelect)
|
||||||
|
await Promise.all([
|
||||||
|
dokterState.findMany.load(),
|
||||||
|
tarifState.findMany.load(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Load data fasilitas
|
||||||
await state.edit.load(id);
|
await state.edit.load(id);
|
||||||
const loadedData = state.edit.form;
|
const loaded = state.edit.form;
|
||||||
|
if (loaded) {
|
||||||
if (!loadedData) {
|
setFormData({
|
||||||
toast.error('Data tidak ditemukan');
|
name: loaded.name,
|
||||||
return;
|
informasiUmum: loaded.informasiUmum,
|
||||||
}
|
layananUnggulan: loaded.layananUnggulan,
|
||||||
|
dokterdanTenagaMedis: loaded.dokterdanTenagaMedis || [],
|
||||||
// Gunakan JSON fallback untuk deep clone
|
fasilitasPendukung: loaded.fasilitasPendukung,
|
||||||
const clonedData = deepClone(loadedData) as FasilitasKesehatanFormBase;
|
prosedurPendaftaran: loaded.prosedurPendaftaran,
|
||||||
|
tarifDanLayanan: loaded.tarifDanLayanan || [],
|
||||||
setFormData(clonedData);
|
});
|
||||||
setOriginalData(clonedData);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
toast.error('Gagal memuat data fasilitas kesehatan');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
load();
|
loadAll();
|
||||||
}, [params?.id]);
|
}, [params?.id]);
|
||||||
|
|
||||||
const handleResetForm = () => {
|
const updateForm = <K extends keyof EditFasilitasKesehatanForm>(
|
||||||
setFormData({
|
field: K,
|
||||||
name: originalData.name,
|
value: EditFasilitasKesehatanForm[K]
|
||||||
informasiUmum:
|
) => {
|
||||||
{
|
setFormData(prev => ({ ...prev, [field]: value }));
|
||||||
fasilitas: originalData.informasiUmum.fasilitas,
|
|
||||||
alamat: originalData.informasiUmum.alamat,
|
|
||||||
jamOperasional: originalData.informasiUmum.jamOperasional
|
|
||||||
},
|
|
||||||
layananUnggulan: { content: originalData.layananUnggulan.content },
|
|
||||||
dokterdanTenagaMedis: {
|
|
||||||
name: originalData.dokterdanTenagaMedis.name,
|
|
||||||
specialist: originalData.dokterdanTenagaMedis.specialist,
|
|
||||||
jadwal: originalData.dokterdanTenagaMedis.jadwal
|
|
||||||
},
|
|
||||||
fasilitasPendukung: { content: originalData.fasilitasPendukung.content },
|
|
||||||
prosedurPendaftaran: { content: originalData.prosedurPendaftaran.content },
|
|
||||||
tarifDanLayanan: {
|
|
||||||
layanan: originalData.tarifDanLayanan.layanan,
|
|
||||||
tarif: originalData.tarifDanLayanan.tarif
|
|
||||||
},
|
|
||||||
});
|
|
||||||
toast.info("Form dikembalikan ke data awal");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Submit
|
const handleReset = () => {
|
||||||
const handleSubmit = async () => {
|
const loaded = state.edit.form;
|
||||||
|
if (loaded) {
|
||||||
|
setFormData({
|
||||||
|
name: loaded.name,
|
||||||
|
informasiUmum: loaded.informasiUmum,
|
||||||
|
layananUnggulan: loaded.layananUnggulan,
|
||||||
|
dokterdanTenagaMedis: loaded.dokterdanTenagaMedis || [],
|
||||||
|
fasilitasPendukung: loaded.fasilitasPendukung,
|
||||||
|
prosedurPendaftaran: loaded.prosedurPendaftaran,
|
||||||
|
tarifDanLayanan: loaded.tarifDanLayanan || [],
|
||||||
|
});
|
||||||
|
toast.info('Form dikembalikan ke data awal');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
try {
|
try {
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
state.edit.form = { ...state.edit.form, ...formData };
|
|
||||||
|
// Update state Valtio
|
||||||
|
state.edit.form = { ...formData };
|
||||||
|
|
||||||
const success = await state.edit.submit();
|
const success = await state.edit.submit();
|
||||||
if (success) {
|
if (success) {
|
||||||
toast.success('Fasilitas kesehatan berhasil diperbarui!');
|
toast.success('Fasilitas kesehatan berhasil diperbarui!');
|
||||||
@@ -159,14 +124,14 @@ function EditFasilitasKesehatan() {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
toast.error('Terjadi kesalahan saat memperbarui data fasilitas kesehatan');
|
toast.error('Gagal memperbarui data');
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
<Box px={{ base: 'sm', md: 'lg' }} py="md" component="form" onSubmit={handleSubmit}>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<Group mb="md">
|
<Group mb="md">
|
||||||
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
@@ -189,7 +154,7 @@ function EditFasilitasKesehatan() {
|
|||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Nama Fasilitas Kesehatan"
|
label="Nama Fasilitas Kesehatan"
|
||||||
placeholder="Masukkan nama fasilitas kesehatan"
|
placeholder="Masukkan nama"
|
||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={(e) => updateForm('name', e.target.value)}
|
onChange={(e) => updateForm('name', e.target.value)}
|
||||||
required
|
required
|
||||||
@@ -197,118 +162,108 @@ function EditFasilitasKesehatan() {
|
|||||||
|
|
||||||
{/* Informasi Umum */}
|
{/* Informasi Umum */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw="bold" mb={5}>
|
<Text fw="bold" mb={5}>Informasi Umum</Text>
|
||||||
Informasi Umum
|
|
||||||
</Text>
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Fasilitas"
|
label="Fasilitas"
|
||||||
value={formData.informasiUmum.fasilitas}
|
value={formData.informasiUmum.fasilitas}
|
||||||
onChange={(e) => updateNested('informasiUmum', 'fasilitas', e.target.value)}
|
onChange={(e) =>
|
||||||
|
updateForm('informasiUmum', {
|
||||||
|
...formData.informasiUmum,
|
||||||
|
fasilitas: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Alamat"
|
label="Alamat"
|
||||||
value={formData.informasiUmum.alamat}
|
value={formData.informasiUmum.alamat}
|
||||||
onChange={(e) => updateNested('informasiUmum', 'alamat', e.target.value)}
|
onChange={(e) =>
|
||||||
|
updateForm('informasiUmum', {
|
||||||
|
...formData.informasiUmum,
|
||||||
|
alamat: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Jam Operasional"
|
label="Jam Operasional"
|
||||||
value={formData.informasiUmum.jamOperasional}
|
value={formData.informasiUmum.jamOperasional}
|
||||||
onChange={(e) => updateNested('informasiUmum', 'jamOperasional', e.target.value)}
|
onChange={(e) =>
|
||||||
|
updateForm('informasiUmum', {
|
||||||
|
...formData.informasiUmum,
|
||||||
|
jamOperasional: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Layanan Unggulan */}
|
{/* Layanan Unggulan */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw="bold" mb={5}>
|
<Text fw="bold" mb={5}>Layanan Unggulan</Text>
|
||||||
Layanan Unggulan
|
|
||||||
</Text>
|
|
||||||
<EditEditor
|
<EditEditor
|
||||||
value={formData.layananUnggulan.content}
|
value={formData.layananUnggulan.content}
|
||||||
onChange={(v) => updateNested('layananUnggulan', 'content', v)}
|
onChange={(v) => updateForm('layananUnggulan', { content: v })}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Dokter dan Tenaga Medis */}
|
{/* Dokter & Tenaga Medis — MultiSelect */}
|
||||||
<Box>
|
<MultiSelect
|
||||||
<Text fw="bold" mb={5}>
|
label="Dokter & Tenaga Medis"
|
||||||
Dokter dan Tenaga Medis
|
placeholder="Pilih dokter/tenaga medis"
|
||||||
</Text>
|
data={
|
||||||
<TextInput
|
dokterState.findMany.data?.map((d) => ({
|
||||||
label="Nama Dokter"
|
value: d.id,
|
||||||
value={formData.dokterdanTenagaMedis.name}
|
label: `${d.name} (${d.specialist})`,
|
||||||
onChange={(e) => updateNested('dokterdanTenagaMedis', 'name', e.target.value)}
|
})) || []
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label="Specialist"
|
|
||||||
value={formData.dokterdanTenagaMedis.specialist}
|
|
||||||
onChange={(e) =>
|
|
||||||
updateNested('dokterdanTenagaMedis', 'specialist', e.target.value)
|
|
||||||
}
|
}
|
||||||
|
value={formData.dokterdanTenagaMedis}
|
||||||
|
onChange={(val) => updateForm('dokterdanTenagaMedis', val)}
|
||||||
|
searchable
|
||||||
|
clearable
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<TextInput
|
|
||||||
label="Jadwal"
|
|
||||||
value={formData.dokterdanTenagaMedis.jadwal}
|
|
||||||
onChange={(e) => updateNested('dokterdanTenagaMedis', 'jadwal', e.target.value)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Fasilitas Pendukung */}
|
{/* Fasilitas Pendukung */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw="bold" mb={5}>
|
<Text fw="bold" mb={5}>Fasilitas Pendukung</Text>
|
||||||
Fasilitas Pendukung
|
|
||||||
</Text>
|
|
||||||
<EditEditor
|
<EditEditor
|
||||||
value={formData.fasilitasPendukung.content}
|
value={formData.fasilitasPendukung.content}
|
||||||
onChange={(v) => updateNested('fasilitasPendukung', 'content', v)}
|
onChange={(v) => updateForm('fasilitasPendukung', { content: v })}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Prosedur Pendaftaran */}
|
{/* Prosedur Pendaftaran */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fw="bold" mb={5}>
|
<Text fw="bold" mb={5}>Prosedur Pendaftaran</Text>
|
||||||
Prosedur Pendaftaran
|
|
||||||
</Text>
|
|
||||||
<EditEditor
|
<EditEditor
|
||||||
value={formData.prosedurPendaftaran.content}
|
value={formData.prosedurPendaftaran.content}
|
||||||
onChange={(v) => updateNested('prosedurPendaftaran', 'content', v)}
|
onChange={(v) => updateForm('prosedurPendaftaran', { content: v })}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Tarif dan Layanan */}
|
{/* Tarif & Layanan — MultiSelect */}
|
||||||
<Box>
|
<MultiSelect
|
||||||
<Text fw="bold" mb={5}>
|
label="Tarif & Layanan"
|
||||||
Tarif dan Layanan
|
placeholder="Pilih layanan"
|
||||||
</Text>
|
data={
|
||||||
<TextInput
|
tarifState.findMany.data?.map((t) => ({
|
||||||
label="Tarif"
|
value: t.id,
|
||||||
value={formData.tarifDanLayanan.tarif}
|
label: `${t.layanan} - ${t.tarif}`,
|
||||||
onChange={(e) => updateNested('tarifDanLayanan', 'tarif', e.target.value)}
|
})) || []
|
||||||
|
}
|
||||||
|
value={formData.tarifDanLayanan}
|
||||||
|
onChange={(val) => updateForm('tarifDanLayanan', val)}
|
||||||
|
searchable
|
||||||
|
clearable
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
<TextInput
|
|
||||||
label="Layanan"
|
|
||||||
value={formData.tarifDanLayanan.layanan}
|
|
||||||
onChange={(e) => updateNested('tarifDanLayanan', 'layanan', e.target.value)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Tombol Simpan */}
|
{/* Aksi */}
|
||||||
<Group justify="right">
|
<Group justify="right">
|
||||||
{/* Tombol Batal */}
|
<Button variant="outline" color="gray" radius="md" onClick={handleReset}>
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
color="gray"
|
|
||||||
radius="md"
|
|
||||||
size="md"
|
|
||||||
onClick={handleResetForm}
|
|
||||||
>
|
|
||||||
Batal
|
Batal
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Tombol Simpan */}
|
|
||||||
<Button
|
<Button
|
||||||
onClick={handleSubmit}
|
type="submit"
|
||||||
radius="md"
|
radius="md"
|
||||||
size="md"
|
|
||||||
style={{
|
style={{
|
||||||
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||||
color: '#fff',
|
color: '#fff',
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ import {
|
|||||||
Paper,
|
Paper,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Stack,
|
Stack,
|
||||||
|
Table,
|
||||||
|
TableTbody,
|
||||||
|
TableTd,
|
||||||
|
TableTh,
|
||||||
|
TableThead,
|
||||||
|
TableTr,
|
||||||
Text
|
Text
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
@@ -108,21 +114,79 @@ function DetailFasilitasKesehatan() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="lg" fw="bold">Dokter & Tenaga Medis</Text>
|
<Text fz="lg" fw="bold" mb="sm">Dokter & Tenaga Medis</Text>
|
||||||
<Text fz="md" fw="bold">Nama</Text>
|
{data.dokterdantenagamedis && data.dokterdantenagamedis.length > 0 ? (
|
||||||
<Text fz="md" c="dimmed">{data.dokterdantenagamedis?.name || '-'}</Text>
|
<Box style={{ overflowX: 'auto', width: '100%' }}>
|
||||||
<Text fz="md" fw="bold">Spesialis</Text>
|
<Table striped highlightOnHover withTableBorder>
|
||||||
<Text fz="md" c="dimmed">{data.dokterdantenagamedis?.specialist || '-'}</Text>
|
<TableThead>
|
||||||
<Text fz="md" fw="bold">Jadwal</Text>
|
<TableTr>
|
||||||
<Text fz="md" c="dimmed">{data.dokterdantenagamedis?.jadwal || '-'}</Text>
|
<TableTh style={{ whiteSpace: 'nowrap' }}>Nama</TableTh>
|
||||||
|
<TableTh style={{ whiteSpace: 'nowrap' }}>Spesialis</TableTh>
|
||||||
|
<TableTh style={{ whiteSpace: 'nowrap' }}>Jadwal</TableTh>
|
||||||
|
<TableTh style={{ whiteSpace: 'nowrap' }}>Jam Operasional</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
{data.dokterdantenagamedis.map((dokter) => (
|
||||||
|
<TableTr key={dokter.id}>
|
||||||
|
<TableTd style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}>
|
||||||
|
{dokter.name || '-'}
|
||||||
|
</TableTd>
|
||||||
|
<TableTd style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}>
|
||||||
|
{dokter.specialist || '-'}
|
||||||
|
</TableTd>
|
||||||
|
<TableTd style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}>
|
||||||
|
{dokter.jadwal || '-'}
|
||||||
|
</TableTd>
|
||||||
|
<TableTd style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}>
|
||||||
|
{dokter.jamBukaOperasional} – {dokter.jamTutupOperasional}
|
||||||
|
{dokter.jadwalLibur && (
|
||||||
|
<>
|
||||||
|
<br />
|
||||||
|
<Text span c="dimmed" fz="xs">
|
||||||
|
Libur: {dokter.jadwalLibur} ({dokter.jamBukaLibur}–{dokter.jamTutupLibur})
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
))}
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Text c="dimmed">Tidak ada dokter atau tenaga medis terdaftar.</Text>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box>
|
<Box mt="xl">
|
||||||
<Text fz="lg" fw="bold">Tarif & Layanan</Text>
|
<Text fz="lg" fw="bold" mb="sm">Tarif & Layanan</Text>
|
||||||
<Text fz="md" fw="bold">Layanan</Text>
|
{data.tarifdanlayanan && data.tarifdanlayanan.length > 0 ? (
|
||||||
<Text fz="md" c="dimmed">{data.tarifdanlayanan?.layanan || '-'}</Text>
|
<Box style={{ overflowX: 'auto', width: '100%' }}>
|
||||||
<Text fz="md" fw="bold">Tarif</Text>
|
<Table striped highlightOnHover withTableBorder>
|
||||||
<Text fz="md" c="dimmed">{data.tarifdanlayanan?.tarif || '-'}</Text>
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh style={{ whiteSpace: 'nowrap' }}>Layanan</TableTh>
|
||||||
|
<TableTh style={{ whiteSpace: 'nowrap' }}>Tarif</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
{data.tarifdanlayanan.map((tarif) => (
|
||||||
|
<TableTr key={tarif.id}>
|
||||||
|
<TableTd style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}>
|
||||||
|
{tarif.layanan || '-'}
|
||||||
|
</TableTd>
|
||||||
|
<TableTd style={{ whiteSpace: 'normal', wordBreak: 'break-word' }}>
|
||||||
|
{tarif.tarif || '-'}
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
))}
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
|
<Text c="dimmed">Tidak ada tarif atau layanan terdaftar.</Text>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Aksi */}
|
{/* Aksi */}
|
||||||
|
|||||||
@@ -7,19 +7,20 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Group,
|
Group,
|
||||||
Loader,
|
Loader,
|
||||||
|
MultiSelect,
|
||||||
Paper,
|
Paper,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Title
|
Title
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconArrowBack } from '@tabler/icons-react';
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
|
||||||
function CreateFasilitasKesehatan() {
|
function CreateFasilitasKesehatan() {
|
||||||
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
|
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -34,23 +35,16 @@ function CreateFasilitasKesehatan() {
|
|||||||
jamOperasional: '',
|
jamOperasional: '',
|
||||||
},
|
},
|
||||||
layananUnggulan: {
|
layananUnggulan: {
|
||||||
content: '',
|
content: ''
|
||||||
},
|
|
||||||
dokterdanTenagaMedis: {
|
|
||||||
name: '',
|
|
||||||
specialist: '',
|
|
||||||
jadwal: '',
|
|
||||||
},
|
},
|
||||||
|
dokterdanTenagaMedis: [] as string[],
|
||||||
fasilitasPendukung: {
|
fasilitasPendukung: {
|
||||||
content: '',
|
content: '',
|
||||||
},
|
},
|
||||||
prosedurPendaftaran: {
|
prosedurPendaftaran: {
|
||||||
content: '',
|
content: '',
|
||||||
},
|
},
|
||||||
tarifDanLayanan: {
|
tarifDanLayanan: [] as string[],
|
||||||
layanan: '',
|
|
||||||
tarif: '',
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -70,6 +64,11 @@ function CreateFasilitasKesehatan() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
fasilitasKesehatanState.dokter.findMany.load();
|
||||||
|
fasilitasKesehatanState.tarif.findMany.load();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box px={{ base: 'sm', md: 'lg' }} py="md" component="form" onSubmit={handleSubmit}>
|
<Box px={{ base: 'sm', md: 'lg' }} py="md" component="form" onSubmit={handleSubmit}>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
@@ -140,31 +139,25 @@ function CreateFasilitasKesehatan() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
|
||||||
{/* Dokter dan Tenaga Medis */}
|
{/* Dokter dan Tenaga Medis */}
|
||||||
<Box>
|
<MultiSelect
|
||||||
<Text fz="md" fw="bold" mb={5}>Dokter dan Tenaga Medis</Text>
|
label="Dokter & Tenaga Medis"
|
||||||
<TextInput
|
placeholder="Pilih dokter / tenaga medis"
|
||||||
label="Nama Dokter"
|
data={
|
||||||
placeholder="Masukkan nama dokter"
|
fasilitasKesehatanState.dokter.findMany.data?.map((item) => ({
|
||||||
value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.name}
|
label: item.name,
|
||||||
onChange={(e) => (stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.name = e.target.value)}
|
value: item.id,
|
||||||
|
})) || []
|
||||||
|
}
|
||||||
|
value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis}
|
||||||
|
onChange={(val: string[]) => {
|
||||||
|
stateFasilitasKesehatan.create.form.dokterdanTenagaMedis = val;
|
||||||
|
}}
|
||||||
|
searchable
|
||||||
|
clearable
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<TextInput
|
|
||||||
label="Spesialis"
|
|
||||||
placeholder="Masukkan spesialis"
|
|
||||||
value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.specialist}
|
|
||||||
onChange={(e) => (stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.specialist = e.target.value)}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label="Jadwal"
|
|
||||||
placeholder="Masukkan jadwal"
|
|
||||||
value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.jadwal}
|
|
||||||
onChange={(e) => (stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.jadwal = e.target.value)}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Fasilitas Pendukung */}
|
{/* Fasilitas Pendukung */}
|
||||||
<Box>
|
<Box>
|
||||||
@@ -175,6 +168,24 @@ function CreateFasilitasKesehatan() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<MultiSelect
|
||||||
|
label="Layanan"
|
||||||
|
placeholder="Pilih layanan"
|
||||||
|
data={
|
||||||
|
fasilitasKesehatanState.tarif.findMany.data?.map((item) => ({
|
||||||
|
label: item.layanan,
|
||||||
|
value: item.id,
|
||||||
|
})) || []
|
||||||
|
}
|
||||||
|
value={stateFasilitasKesehatan.create.form.tarifDanLayanan} // string[]
|
||||||
|
onChange={(val: string[]) => {
|
||||||
|
stateFasilitasKesehatan.create.form.tarifDanLayanan = val;
|
||||||
|
}}
|
||||||
|
searchable
|
||||||
|
clearable
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Prosedur Pendaftaran */}
|
{/* Prosedur Pendaftaran */}
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz="md" fw="bold" mb={5}>Prosedur Pendaftaran</Text>
|
<Text fz="md" fw="bold" mb={5}>Prosedur Pendaftaran</Text>
|
||||||
@@ -184,24 +195,6 @@ function CreateFasilitasKesehatan() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Tarif dan Layanan */}
|
|
||||||
<Box>
|
|
||||||
<Text fz="md" fw="bold" mb={5}>Tarif dan Layanan</Text>
|
|
||||||
<TextInput
|
|
||||||
label="Tarif"
|
|
||||||
placeholder="Masukkan tarif"
|
|
||||||
value={stateFasilitasKesehatan.create.form.tarifDanLayanan.tarif}
|
|
||||||
onChange={(e) => (stateFasilitasKesehatan.create.form.tarifDanLayanan.tarif = e.target.value)}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label="Layanan"
|
|
||||||
placeholder="Masukkan layanan"
|
|
||||||
value={stateFasilitasKesehatan.create.form.tarifDanLayanan.layanan}
|
|
||||||
onChange={(e) => (stateFasilitasKesehatan.create.form.tarifDanLayanan.layanan = e.target.value)}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Submit */}
|
{/* Submit */}
|
||||||
<Group justify="right">
|
<Group justify="right">
|
||||||
|
|||||||
@@ -1,11 +1,241 @@
|
|||||||
import React from 'react';
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import {
|
||||||
|
ActionIcon,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Loader,
|
||||||
|
Paper,
|
||||||
|
Stack,
|
||||||
|
TextInput,
|
||||||
|
Title
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { TimeInput } from '@mantine/dates';
|
||||||
|
import { IconArrowBack, IconClock } from '@tabler/icons-react';
|
||||||
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
|
||||||
|
function EditDokterTenagaMedis() {
|
||||||
|
const state = useProxy(fasilitasKesehatanState.dokter);
|
||||||
|
const router = useRouter();
|
||||||
|
const params = useParams();
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
name: '',
|
||||||
|
specialist: '',
|
||||||
|
jadwal: '',
|
||||||
|
jadwalLibur: '',
|
||||||
|
jamBukaOperasional: '',
|
||||||
|
jamTutupOperasional: '',
|
||||||
|
jamBukaLibur: '',
|
||||||
|
jamTutupLibur: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const [originalData, setOriginalData] = useState({
|
||||||
|
name: '',
|
||||||
|
specialist: '',
|
||||||
|
jadwal: '',
|
||||||
|
jadwalLibur: '',
|
||||||
|
jamBukaOperasional: '',
|
||||||
|
jamTutupOperasional: '',
|
||||||
|
jamBukaLibur: '',
|
||||||
|
jamTutupLibur: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load data
|
||||||
|
useEffect(() => {
|
||||||
|
const load = async () => {
|
||||||
|
const id = params?.id as string;
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await state.update.load(id);
|
||||||
|
const loadedData = state.update.form;
|
||||||
|
|
||||||
|
if (!loadedData) {
|
||||||
|
toast.error('Data tidak ditemukan');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFormData(loadedData);
|
||||||
|
setOriginalData(loadedData);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
toast.error('Gagal memuat data fasilitas kesehatan');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
load();
|
||||||
|
}, [params?.id]);
|
||||||
|
|
||||||
|
const handleResetForm = () => {
|
||||||
|
setFormData({
|
||||||
|
name: originalData.name,
|
||||||
|
specialist: originalData.specialist,
|
||||||
|
jadwal: originalData.jadwal,
|
||||||
|
jadwalLibur: originalData.jadwalLibur,
|
||||||
|
jamBukaOperasional: originalData.jamBukaOperasional,
|
||||||
|
jamTutupOperasional: originalData.jamTutupOperasional,
|
||||||
|
jamBukaLibur: originalData.jamBukaLibur,
|
||||||
|
jamTutupLibur: originalData.jamTutupLibur,
|
||||||
|
});
|
||||||
|
toast.info("Form dikembalikan ke data awal");
|
||||||
|
};
|
||||||
|
|
||||||
|
const refBuka = useRef<HTMLInputElement>(null);
|
||||||
|
const refTutup = useRef<HTMLInputElement>(null);
|
||||||
|
const refBukaLibur = useRef<HTMLInputElement>(null);
|
||||||
|
const refTutupLibur = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const picker = (ref: any) => (
|
||||||
|
<ActionIcon variant="subtle" color="gray" onClick={() => ref.current?.showPicker()}>
|
||||||
|
<IconClock size={16} stroke={1.5} />
|
||||||
|
</ActionIcon>
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleChange = (field: string, value: string) => {
|
||||||
|
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Submit
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
state.update.form = { ...state.update.form, ...formData };
|
||||||
|
const success = await state.update.submit();
|
||||||
|
if (success) {
|
||||||
|
toast.success('Data berhasil diperbarui!');
|
||||||
|
router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
toast.error('Terjadi kesalahan saat memperbarui data');
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function Page() {
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||||
Page
|
{/* Header */}
|
||||||
</div>
|
<Group mb="md">
|
||||||
|
<Button variant="subtle" onClick={() => router.back()} p="xs" radius="md">
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
</Button>
|
||||||
|
<Title order={4} ml="sm" c="dark">
|
||||||
|
Edit Fasilitas Kesehatan
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* Form */}
|
||||||
|
<Paper
|
||||||
|
w={{ base: '100%', md: '50%' }}
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
style={{ border: '1px solid #e0e0e0' }}
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
|
<TextInput
|
||||||
|
label="Nama Dokter"
|
||||||
|
placeholder="Masukkan nama dokter"
|
||||||
|
value={formData.name}
|
||||||
|
onChange={(e) => handleChange("name", e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
label="Jadwal"
|
||||||
|
placeholder="Masukkan jadwal"
|
||||||
|
value={formData.jadwal}
|
||||||
|
onChange={(e) => handleChange("jadwal", e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
label="Jadwal Libur"
|
||||||
|
placeholder="Masukkan jadwal libur"
|
||||||
|
value={formData.jadwalLibur}
|
||||||
|
onChange={(e) => handleChange("jadwalLibur", e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TimeInput
|
||||||
|
label="Jam Buka Operasional"
|
||||||
|
ref={refBuka}
|
||||||
|
rightSection={picker(refBuka)}
|
||||||
|
value={formData.jamBukaOperasional}
|
||||||
|
onChange={(e) => handleChange("jamBukaOperasional", e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TimeInput
|
||||||
|
label="Jam Tutup Operasional"
|
||||||
|
ref={refTutup}
|
||||||
|
rightSection={picker(refTutup)}
|
||||||
|
value={formData.jamTutupOperasional}
|
||||||
|
onChange={(e) => handleChange("jamTutupOperasional", e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TimeInput
|
||||||
|
label="Jam Buka Hari Libur"
|
||||||
|
ref={refBukaLibur}
|
||||||
|
rightSection={picker(refBukaLibur)}
|
||||||
|
value={formData.jamBukaLibur}
|
||||||
|
onChange={(e) => handleChange("jamBukaLibur", e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TimeInput
|
||||||
|
label="Jam Tutup Hari Libur"
|
||||||
|
ref={refTutupLibur}
|
||||||
|
rightSection={picker(refTutupLibur)}
|
||||||
|
value={formData.jamTutupLibur}
|
||||||
|
onChange={(e) => handleChange("jamTutupLibur", e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
{/* Tombol Simpan */}
|
||||||
|
<Group justify="right">
|
||||||
|
{/* Tombol Batal */}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
color="gray"
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
onClick={handleResetForm}
|
||||||
|
>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{/* Tombol Simpan */}
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||||
|
color: '#fff',
|
||||||
|
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
export default EditDokterTenagaMedis;
|
||||||
|
|||||||
@@ -1,11 +1,165 @@
|
|||||||
import React from 'react';
|
'use client'
|
||||||
|
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
|
||||||
|
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Paper,
|
||||||
|
Skeleton,
|
||||||
|
Stack,
|
||||||
|
Text
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react';
|
||||||
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
function Page() {
|
function DetailDokterTenagaMedis() {
|
||||||
|
const params = useParams();
|
||||||
|
const router = useRouter();
|
||||||
|
const state = useProxy(fasilitasKesehatanState.dokter);
|
||||||
|
const [modalHapus, setModalHapus] = useState(false);
|
||||||
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
state.findUnique.load(params?.id as string);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleHapus = () => {
|
||||||
|
if (selectedId) {
|
||||||
|
state.delete.byId(selectedId);
|
||||||
|
setModalHapus(false);
|
||||||
|
setSelectedId(null);
|
||||||
|
router.push(
|
||||||
|
'/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!state.findUnique.data) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack py={10}>
|
||||||
Page
|
<Skeleton height={500} radius="md" />
|
||||||
</div>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
const data = state.findUnique.data;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
{/* Tombol Back */}
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
onClick={() => router.back()}
|
||||||
|
leftSection={<IconArrowBack size={24} color={colors['blue-button']} />}
|
||||||
|
mb={15}
|
||||||
|
>
|
||||||
|
Kembali
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{/* Wrapper Detail */}
|
||||||
|
<Paper
|
||||||
|
withBorder
|
||||||
|
w={{ base: '100%', md: '70%' }}
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
|
<Text fz="2xl" fw="bold" c={colors['blue-button']}>
|
||||||
|
Detail Dokter & Tenaga Medis
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Paper bg="#ECEEF8" p="md" radius="md" shadow="xs">
|
||||||
|
<Stack gap="sm">
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Nama Dokter</Text>
|
||||||
|
<Text fz="md" c="dimmed">{data.name || '-'}</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="md" fw="bold">Specialist</Text>
|
||||||
|
<Text fz="md" c="dimmed">{data.specialist || '-'}</Text>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Jadwal</Text>
|
||||||
|
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data.jadwal || '-' }} />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Jadwal Libur</Text>
|
||||||
|
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data.jadwalLibur || '-' }} />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Jam Buka Operasional</Text>
|
||||||
|
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data.jamBukaOperasional || '-' }} />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Jam Tutup Operasional</Text>
|
||||||
|
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data.jamTutupOperasional || '-' }} />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Jam Buka Libur</Text>
|
||||||
|
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data.jamBukaLibur || '-' }} />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="lg" fw="bold">Jam Tutup Libur</Text>
|
||||||
|
<Text fz="md" c="dimmed" style={{ wordBreak: "break-word", whiteSpace: "normal" }} dangerouslySetInnerHTML={{ __html: data.jamTutupLibur || '-' }} />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Aksi */}
|
||||||
|
<Group gap="sm">
|
||||||
|
<Button
|
||||||
|
color="red"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(data.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}}
|
||||||
|
variant="light"
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<IconTrash size={20} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
color="green"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis/${data.id}/edit`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
variant="light"
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Konfirmasi Hapus */}
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text="Apakah anda yakin ingin menghapus dokter & tenaga medis ini?"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailDokterTenagaMedis;
|
||||||
|
|||||||
@@ -1,71 +1,184 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
'use client'
|
'use client'
|
||||||
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
|
||||||
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
import { ActionIcon, Box, Button, Group, Loader, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||||
import { IconArrowBack } from '@tabler/icons-react';
|
import { TimeInput } from '@mantine/dates';
|
||||||
import { useParams, useRouter } from 'next/navigation';
|
import { IconArrowBack, IconClock } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useRef, useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
|
||||||
function CreateDokter() {
|
function CreateDokter() {
|
||||||
const params = useParams()
|
|
||||||
const createState = useProxy(fasilitasKesehatanState.dokter)
|
const createState = useProxy(fasilitasKesehatanState.dokter)
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
createState.create.create.form = {
|
createState.create.create.form = {
|
||||||
name: "",
|
name: "",
|
||||||
specialist: "",
|
specialist: "",
|
||||||
jadwal: "",
|
jadwal: "",
|
||||||
|
jadwalLibur: "",
|
||||||
|
jamBukaOperasional: "",
|
||||||
|
jamTutupOperasional: "",
|
||||||
|
jamBukaLibur: "",
|
||||||
|
jamTutupLibur: "",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const refBuka = useRef<HTMLInputElement>(null);
|
||||||
|
const refTutup = useRef<HTMLInputElement>(null);
|
||||||
|
const refBukaLibur = useRef<HTMLInputElement>(null);
|
||||||
|
const refTutupLibur = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
const picker = (ref: any) => (
|
||||||
|
<ActionIcon variant="subtle" color="gray" onClick={() => ref.current?.showPicker()}>
|
||||||
|
<IconClock size={16} stroke={1.5} />
|
||||||
|
</ActionIcon>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setIsSubmitting(true);
|
||||||
await createState.create.create.create();
|
await createState.create.create.create();
|
||||||
|
toast.success('Data berhasil disimpan');
|
||||||
resetForm();
|
resetForm();
|
||||||
router.push(`/admin/kesehatan/fasilitas-kesehatan/${params?.id}/dokter-tenaga-medis`)
|
router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
toast.error('Gagal menyimpan data');
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Box component="form" onSubmit={handleSubmit}>
|
<Box px={{ base: 'sm', md: 'lg' }} py="md" component="form" onSubmit={handleSubmit}>
|
||||||
<Box mb={10}>
|
{/* Header */}
|
||||||
<Button variant="subtle" onClick={() => router.back()}>
|
<Group mb="md">
|
||||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
onClick={() => router.back()}
|
||||||
|
p="xs"
|
||||||
|
radius="md"
|
||||||
|
>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
<Title order={4} ml="sm" c="dark">
|
||||||
|
Tambah Data Dokter & Tenaga Medis
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
|
{/* Form */}
|
||||||
<Stack gap="xs">
|
<Paper
|
||||||
<Title order={3}>Create Dokter</Title>
|
w={{ base: '100%', md: '50%' }}
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
style={{ border: '1px solid #e0e0e0' }}
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fz="sm" fw="bold">Nama Dokter</Text>}
|
label={"Nama Dokter"}
|
||||||
placeholder="masukkan nama dokter"
|
placeholder="Masukkan nama dokter"
|
||||||
value={createState.create.create.form.name}
|
value={createState.create.create.form.name}
|
||||||
onChange={(e) => {
|
onChange={(e) => (createState.create.create.form.name = e.target.value)}
|
||||||
createState.create.create.form.name = e.target.value;
|
required
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Text fz="md" fw="bold">Specialist</Text>
|
|
||||||
|
{/* Informasi Umum */}
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
label={<Text fz="sm" fw="bold">Specialist</Text>}
|
label="Specialist"
|
||||||
placeholder="masukkan specialist"
|
placeholder="Masukkan specialist"
|
||||||
value={createState.create.create.form.specialist}
|
value={createState.create.create.form.specialist}
|
||||||
onChange={(e) => {
|
onChange={(e) => (createState.create.create.form.specialist = e.target.value)}
|
||||||
createState.create.create.form.specialist = e.target.value;
|
required
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Box>
|
|
||||||
<Text fz="md" fw="bold">Jadwal</Text>
|
<TextInput
|
||||||
<CreateEditor
|
label="Jadwal"
|
||||||
|
placeholder="Masukkan jadwal"
|
||||||
value={createState.create.create.form.jadwal}
|
value={createState.create.create.form.jadwal}
|
||||||
onChange={(htmlContent) => {
|
onChange={(e) => (createState.create.create.form.jadwal = e.target.value)}
|
||||||
createState.create.create.form.jadwal = htmlContent;
|
required
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
|
||||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>
|
<TextInput
|
||||||
Simpan
|
label="Jadwal Libur"
|
||||||
|
placeholder="Masukkan jadwal libur"
|
||||||
|
value={createState.create.create.form.jadwalLibur}
|
||||||
|
onChange={(e) => (createState.create.create.form.jadwalLibur = e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TimeInput
|
||||||
|
label="Jam Buka Operasional"
|
||||||
|
ref={refBuka}
|
||||||
|
rightSection={picker(refBuka)}
|
||||||
|
value={createState.create.create.form.jamBukaOperasional}
|
||||||
|
onChange={(e) => (createState.create.create.form.jamBukaOperasional = e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TimeInput
|
||||||
|
label="Jam Tutup Operasional"
|
||||||
|
ref={refTutup}
|
||||||
|
rightSection={picker(refTutup)}
|
||||||
|
value={createState.create.create.form.jamTutupOperasional}
|
||||||
|
onChange={(e) => (createState.create.create.form.jamTutupOperasional = e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TimeInput
|
||||||
|
label="Jam Buka Hari Libur"
|
||||||
|
ref={refBukaLibur}
|
||||||
|
rightSection={picker(refBukaLibur)}
|
||||||
|
value={createState.create.create.form.jamBukaLibur}
|
||||||
|
onChange={(e) => (createState.create.create.form.jamBukaLibur = e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TimeInput
|
||||||
|
label="Jam Tutup Hari Libur"
|
||||||
|
ref={refTutupLibur}
|
||||||
|
rightSection={picker(refTutupLibur)}
|
||||||
|
value={createState.create.create.form.jamTutupLibur}
|
||||||
|
onChange={(e) => (createState.create.create.form.jamTutupLibur = e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Submit */}
|
||||||
|
<Group justify="right">
|
||||||
|
{/* Tombol Batal */}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
color="gray"
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
onClick={resetForm}
|
||||||
|
>
|
||||||
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
{/* Tombol Simpan */}
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||||
|
color: '#fff',
|
||||||
|
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconArrowBack, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
import { IconArrowBack, IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
import HeaderSearch from '@/app/admin/(dashboard)/_com/header';
|
import HeaderSearch from '@/app/admin/(dashboard)/_com/header';
|
||||||
import JudulList from '@/app/admin/(dashboard)/_com/judulList';
|
|
||||||
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
@@ -18,7 +17,7 @@ function DokterTenagaMedis() {
|
|||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Box mb={10}>
|
<Box mb={10}>
|
||||||
<Button variant="subtle" onClick={() => router.back()}>
|
<Button variant="subtle" onClick={() => router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan')}>
|
||||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -60,49 +59,101 @@ function ListDokterTenagaMedis({ search }: { search: string }) {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Paper withBorder bg={colors['white-1']} p={'lg'} shadow="md" radius="md">
|
||||||
<Stack>
|
{/* Judul + Tombol Tambah */}
|
||||||
<JudulList
|
<Group justify="space-between" mb="md">
|
||||||
title='List Fasilitas Kesehatan'
|
<Title order={4}>Daftar Dokter dan Tenaga Medis</Title>
|
||||||
href={`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis/create`}
|
<Button
|
||||||
/>
|
leftSection={<IconPlus size={18} />}
|
||||||
|
color="blue"
|
||||||
|
variant="light"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
'/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis/create'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Tambah Baru
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* Tabel */}
|
||||||
<Box style={{ overflowX: "auto" }}>
|
<Box style={{ overflowX: "auto" }}>
|
||||||
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
<Table highlightOnHover>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh>Fasilitas Kesehatan</TableTh>
|
<TableTh>Nama Dokter</TableTh>
|
||||||
<TableTh>Alamat</TableTh>
|
<TableTh>Spesialis</TableTh>
|
||||||
<TableTh>Jam Operasional</TableTh>
|
<TableTh>Jadwal</TableTh>
|
||||||
<TableTh>Detail</TableTh>
|
<TableTh>Aksi</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
{filteredData.map((item) => (
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>{item.name}</TableTd>
|
|
||||||
<TableTd>{item.specialist}</TableTd>
|
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text dangerouslySetInnerHTML={{ __html: item.jadwal }} />
|
<Box w={150}>
|
||||||
|
<Text fw={500} truncate="end" lineClamp={1}>
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/${item.id}`)}>
|
<Box w={150}>
|
||||||
<IconDeviceImacCog size={25} />
|
{item.specialist || '-'}
|
||||||
|
</Box>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Box w={150}>
|
||||||
|
<Text dangerouslySetInnerHTML={{ __html: item.jadwal || '-' }} />
|
||||||
|
</Box>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="blue"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconDeviceImacCog size={20} />
|
||||||
|
<Text ml={5}>Detail</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
))}
|
))
|
||||||
|
) : (
|
||||||
|
<TableTr>
|
||||||
|
<TableTd colSpan={4}>
|
||||||
|
<Center py={20}>
|
||||||
|
<Text c="dimmed">
|
||||||
|
Tidak ada fasilitas kesehatan yang cocok
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
)}
|
||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
{/* Pagination */}
|
||||||
<Center>
|
<Center>
|
||||||
<Pagination
|
<Pagination
|
||||||
value={page}
|
value={page}
|
||||||
onChange={(newPage) => load(newPage)} // ini penting!
|
onChange={(newPage) => {
|
||||||
|
load(newPage, 10, search);
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
}}
|
||||||
total={totalPages}
|
total={totalPages}
|
||||||
mt="md"
|
mt="md"
|
||||||
mb="md"
|
mb="md"
|
||||||
|
color="blue"
|
||||||
|
radius="md"
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import {
|
import {
|
||||||
|
ActionIcon,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
|
Grid,
|
||||||
|
GridCol,
|
||||||
Group,
|
Group,
|
||||||
Pagination,
|
Pagination,
|
||||||
Paper,
|
Paper,
|
||||||
@@ -16,30 +19,52 @@ import {
|
|||||||
TableThead,
|
TableThead,
|
||||||
TableTr,
|
TableTr,
|
||||||
Text,
|
Text,
|
||||||
Title
|
TextInput,
|
||||||
|
Title,
|
||||||
|
Tooltip
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconDeviceImacCog, IconPlus, IconSearch } from '@tabler/icons-react';
|
import { IconCoin, IconDeviceImacCog, IconPlus, IconReportMedical, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import HeaderSearch from '../../../_com/header';
|
|
||||||
import fasilitasKesehatanState from '../../../_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
import fasilitasKesehatanState from '../../../_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||||
|
|
||||||
|
|
||||||
function FasilitasKesehatan() {
|
function FasilitasKesehatan() {
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
const router = useRouter()
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{/* Header Search */}
|
<Grid mb={10}>
|
||||||
<HeaderSearch
|
<GridCol span={{ base: 12, md: 8 }}>
|
||||||
title='Fasilitas Kesehatan'
|
<Title order={3}>Fasilitas Kesehatan</Title>
|
||||||
|
</GridCol>
|
||||||
|
<GridCol span={{ base: 12, md: 4 }}>
|
||||||
|
<Group gap={"xs"}>
|
||||||
|
<Tooltip label="List Dokter" withArrow>
|
||||||
|
<ActionIcon onClick={() => router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis')} size="lg" radius="xl" color="green.6">
|
||||||
|
<IconReportMedical size={20} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip label="List Tarif Layanan" withArrow>
|
||||||
|
<ActionIcon onClick={()=> router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/tarif-layanan')} size="lg" radius="xl" color="blue.6">
|
||||||
|
<IconCoin size={20} />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
<Paper radius="lg" bg={colors['white-1']}>
|
||||||
|
<TextInput
|
||||||
|
radius="lg"
|
||||||
placeholder='Cari nama, alamat, atau jam operasional...'
|
placeholder='Cari nama, alamat, atau jam operasional...'
|
||||||
searchIcon={<IconSearch size={20} />}
|
leftSection={<IconSearch size={20} />}
|
||||||
|
w="133%"
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
|
</Paper>
|
||||||
|
</Group>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<ListFasilitasKesehatan search={search} />
|
<ListFasilitasKesehatan search={search} />
|
||||||
</Box>
|
</Box>
|
||||||
@@ -54,6 +79,7 @@ function ListFasilitasKesehatan({ search }: { search: string }) {
|
|||||||
const { data, page, totalPages, loading, load } = stateFasilitasKesehatan.findMany;
|
const { data, page, totalPages, loading, load } = stateFasilitasKesehatan.findMany;
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
|
|
||||||
load(page, 10, search);
|
load(page, 10, search);
|
||||||
}, [page, search]);
|
}, [page, search]);
|
||||||
|
|
||||||
@@ -93,8 +119,8 @@ function ListFasilitasKesehatan({ search }: { search: string }) {
|
|||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh>Fasilitas Kesehatan</TableTh>
|
<TableTh>Fasilitas Kesehatan</TableTh>
|
||||||
<TableTh>Dokter</TableTh>
|
<TableTh>Jumlah Dokter</TableTh>
|
||||||
<TableTh>Layanan</TableTh>
|
<TableTh>Jumlah Layanan</TableTh>
|
||||||
<TableTh>Aksi</TableTh>
|
<TableTh>Aksi</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
@@ -111,13 +137,17 @@ function ListFasilitasKesehatan({ search }: { search: string }) {
|
|||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Box w={150}>
|
<Box w={150}>
|
||||||
{item.dokterdantenagamedis?.name || '-'}
|
{item.dokterdantenagamedis?.length
|
||||||
|
? `${item.dokterdantenagamedis.length} dokter`
|
||||||
|
: '-'}
|
||||||
</Box>
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Box w={150}>
|
<Box w={150}>
|
||||||
<Text truncate="end" lineClamp={1}>
|
<Text truncate="end" lineClamp={1}>
|
||||||
{item.tarifdanlayanan?.layanan || '-'}
|
{item.tarifdanlayanan?.length
|
||||||
|
? `${item.tarifdanlayanan.length} layanan`
|
||||||
|
: '-'}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
@@ -141,7 +171,7 @@ function ListFasilitasKesehatan({ search }: { search: string }) {
|
|||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={4}>
|
<TableTd colSpan={4}>
|
||||||
<Center py={20}>
|
<Center py={20}>
|
||||||
<Text color="dimmed">
|
<Text c="dimmed">
|
||||||
Tidak ada fasilitas kesehatan yang cocok
|
Tidak ada fasilitas kesehatan yang cocok
|
||||||
</Text>
|
</Text>
|
||||||
</Center>
|
</Center>
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Loader,
|
||||||
|
Paper,
|
||||||
|
Stack,
|
||||||
|
TextInput,
|
||||||
|
Title
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
function EditTarifLayanan() {
|
||||||
|
const editState = useProxy(fasilitasKesehatanState.tarif);
|
||||||
|
const router = useRouter();
|
||||||
|
const params = useParams();
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
|
const [originalData, setOriginalData] = useState({
|
||||||
|
tarif: '',
|
||||||
|
layanan: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
tarif: '',
|
||||||
|
layanan: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadTarifLayanan = async () => {
|
||||||
|
const id = params?.id as string;
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await editState.update.load(id);
|
||||||
|
if (data) {
|
||||||
|
setFormData({
|
||||||
|
tarif: data.tarif || '',
|
||||||
|
layanan: data.layanan || '',
|
||||||
|
});
|
||||||
|
setOriginalData({
|
||||||
|
tarif: data.tarif || '',
|
||||||
|
layanan: data.layanan || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading tarif layanan:', error);
|
||||||
|
toast.error('Gagal memuat data tarif layanan');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadTarifLayanan();
|
||||||
|
}, [params?.id]);
|
||||||
|
|
||||||
|
const handleChange = (field: string, value: string) => {
|
||||||
|
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResetForm = () => {
|
||||||
|
setFormData({
|
||||||
|
tarif: originalData.tarif,
|
||||||
|
layanan: originalData.layanan,
|
||||||
|
});
|
||||||
|
toast.info('Form dikembalikan ke data awal');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
// update global state hanya saat submit
|
||||||
|
editState.update.form = {
|
||||||
|
...editState.update.form,
|
||||||
|
tarif: formData.tarif,
|
||||||
|
layanan: formData.layanan,
|
||||||
|
};
|
||||||
|
|
||||||
|
await editState.update.submit();
|
||||||
|
toast.success('Tarif Layanan berhasil diperbarui!');
|
||||||
|
router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/tarif-layanan');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating tarif layanan:', error);
|
||||||
|
toast.error('Terjadi kesalahan saat memperbarui tarif layanan');
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||||
|
{/* Back Button + Title */}
|
||||||
|
<Group mb="md">
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
onClick={() => router.back()}
|
||||||
|
p="xs"
|
||||||
|
radius="md"
|
||||||
|
>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
</Button>
|
||||||
|
<Title order={4} ml="sm" c="dark">
|
||||||
|
Edit Tarif Layanan
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* Form Wrapper */}
|
||||||
|
<Paper
|
||||||
|
w={{ base: '100%', md: '50%' }}
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
style={{ border: '1px solid #e0e0e0' }}
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
|
<TextInput
|
||||||
|
name="layanan"
|
||||||
|
label="Layanan"
|
||||||
|
placeholder="Masukkan nama layanan"
|
||||||
|
value={formData.layanan}
|
||||||
|
onChange={(e) => handleChange('layanan', e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
name="tarif"
|
||||||
|
label="Tarif"
|
||||||
|
placeholder="Masukkan tarif layanan"
|
||||||
|
value={formData.tarif}
|
||||||
|
onChange={(e) => handleChange('tarif', e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Group justify="right">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
color="gray"
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
onClick={handleResetForm}
|
||||||
|
>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{/* Tombol Simpan */}
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||||
|
color: '#fff',
|
||||||
|
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditTarifLayanan;
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
'use client';
|
||||||
|
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Group,
|
||||||
|
Loader,
|
||||||
|
Paper,
|
||||||
|
Stack,
|
||||||
|
TextInput,
|
||||||
|
Title
|
||||||
|
} from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
function CreateTarifLayanan() {
|
||||||
|
const createState = useProxy(fasilitasKesehatanState.tarif);
|
||||||
|
const router = useRouter();
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
createState.create.form = {
|
||||||
|
tarif: '',
|
||||||
|
layanan: '',
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
try {
|
||||||
|
await createState.create.create();
|
||||||
|
resetForm();
|
||||||
|
router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/tarif-layanan');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating tarif layanan:', error);
|
||||||
|
toast.error('Gagal menambahkan tarif layanan');
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||||
|
{/* Header dengan back button */}
|
||||||
|
<Group mb="md">
|
||||||
|
<Button
|
||||||
|
variant="subtle"
|
||||||
|
onClick={() => router.back()}
|
||||||
|
p="xs"
|
||||||
|
radius="md"
|
||||||
|
>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={24} />
|
||||||
|
</Button>
|
||||||
|
<Title order={4} ml="sm" c="dark">
|
||||||
|
Tambah Tarif Layanan
|
||||||
|
</Title>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* Form utama */}
|
||||||
|
<Paper
|
||||||
|
w={{ base: '100%', md: '50%' }}
|
||||||
|
bg={colors['white-1']}
|
||||||
|
p="lg"
|
||||||
|
radius="md"
|
||||||
|
shadow="sm"
|
||||||
|
style={{ border: '1px solid #e0e0e0' }}
|
||||||
|
>
|
||||||
|
<Stack gap="md">
|
||||||
|
<TextInput
|
||||||
|
label="Layanan"
|
||||||
|
placeholder="Masukkan nama layanan"
|
||||||
|
value={createState.create.form.layanan || ''}
|
||||||
|
onChange={(e) => (createState.create.form.layanan = e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label="Tarif"
|
||||||
|
placeholder="Masukkan tarif"
|
||||||
|
value={createState.create.form.tarif || ''}
|
||||||
|
onChange={(e) => (createState.create.form.tarif = e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Group justify="right">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
color="gray"
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
onClick={resetForm}
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{/* Tombol Simpan */}
|
||||||
|
<Button
|
||||||
|
onClick={handleSubmit}
|
||||||
|
radius="md"
|
||||||
|
size="md"
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, ${colors['blue-button']}, #4facfe)`,
|
||||||
|
color: '#fff',
|
||||||
|
boxShadow: '0 4px 15px rgba(79, 172, 254, 0.4)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isSubmitting ? <Loader size="sm" color="white" /> : 'Simpan'}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateTarifLayanan;
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconArrowBack, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
import { IconArrowBack, IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
import HeaderSearch from '@/app/admin/(dashboard)/_com/header';
|
import HeaderSearch from '@/app/admin/(dashboard)/_com/header';
|
||||||
import JudulList from '@/app/admin/(dashboard)/_com/judulList';
|
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
|
||||||
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
@@ -18,12 +18,12 @@ function TarifLayanan() {
|
|||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Box mb={10}>
|
<Box mb={10}>
|
||||||
<Button variant="subtle" onClick={() => router.back()}>
|
<Button variant="subtle" onClick={() => router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan')}>
|
||||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<HeaderSearch
|
<HeaderSearch
|
||||||
title='Dokter dan Tenaga Medis'
|
title='Tarif dan Layanan'
|
||||||
placeholder='pencarian'
|
placeholder='pencarian'
|
||||||
searchIcon={<IconSearch size={20} />}
|
searchIcon={<IconSearch size={20} />}
|
||||||
value={search}
|
value={search}
|
||||||
@@ -35,8 +35,11 @@ function TarifLayanan() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ListTarifLayanan({ search }: { search: string }) {
|
function ListTarifLayanan({ search }: { search: string }) {
|
||||||
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.dokter)
|
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.tarif);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [modalHapus, setModalHapus] = useState(false);
|
||||||
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
loading,
|
loading,
|
||||||
@@ -49,6 +52,15 @@ function ListTarifLayanan({ search }: { search: string }) {
|
|||||||
load(page, 10, search)
|
load(page, 10, search)
|
||||||
}, [page, search])
|
}, [page, search])
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
if (selectedId) {
|
||||||
|
stateFasilitasKesehatan.delete.byId(selectedId);
|
||||||
|
setModalHapus(false);
|
||||||
|
setSelectedId(null);
|
||||||
|
load(page, 10, search);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const filteredData = data || []
|
const filteredData = data || []
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
@@ -60,51 +72,116 @@ function ListTarifLayanan({ search }: { search: string }) {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Paper withBorder bg={colors['white-1']} p={'lg'} shadow="md" radius="md">
|
||||||
<Stack>
|
{/* Judul + Tombol Tambah */}
|
||||||
<JudulList
|
<Group justify="space-between" mb="md">
|
||||||
title='List Fasilitas Kesehatan'
|
<Title order={4}>Daftar Tarif dan Layanan</Title>
|
||||||
href={`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis/create`}
|
<Button
|
||||||
/>
|
leftSection={<IconPlus size={18} />}
|
||||||
|
color="blue"
|
||||||
|
variant="light"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
'/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/tarif-layanan/create'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Tambah Baru
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* Tabel */}
|
||||||
<Box style={{ overflowX: "auto" }}>
|
<Box style={{ overflowX: "auto" }}>
|
||||||
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
<Table highlightOnHover>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh>Fasilitas Kesehatan</TableTh>
|
<TableTh>Layanan</TableTh>
|
||||||
<TableTh>Alamat</TableTh>
|
<TableTh>Tarif</TableTh>
|
||||||
<TableTh>Jam Operasional</TableTh>
|
<TableTh>Edit</TableTh>
|
||||||
<TableTh>Detail</TableTh>
|
<TableTh>Hapus</TableTh>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
{filteredData.map((item) => (
|
{filteredData.length > 0 ? (
|
||||||
|
filteredData.map((item) => (
|
||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>{item.name}</TableTd>
|
|
||||||
<TableTd>{item.specialist}</TableTd>
|
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Text dangerouslySetInnerHTML={{ __html: item.jadwal }} />
|
<Box w={150}>
|
||||||
|
{item.layanan || '-'}
|
||||||
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/${item.id}`)}>
|
<Box w={150}>
|
||||||
<IconDeviceImacCog size={25} />
|
<Text fw={500} truncate="end" lineClamp={1}>
|
||||||
|
{item.tarif}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="green"
|
||||||
|
onClick={() =>
|
||||||
|
router.push(
|
||||||
|
`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/tarif-layanan/${item.id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconEdit size={18} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
color="red"
|
||||||
|
disabled={stateFasilitasKesehatan.delete.loading}
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedId(item.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconTrash size={18} />
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
))}
|
))
|
||||||
|
) : (
|
||||||
|
<TableTr>
|
||||||
|
<TableTd colSpan={4}>
|
||||||
|
<Center py={20}>
|
||||||
|
<Text c="dimmed">
|
||||||
|
Tidak ada fasilitas kesehatan yang cocok
|
||||||
|
</Text>
|
||||||
|
</Center>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
)}
|
||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
{/* Pagination */}
|
||||||
<Center>
|
<Center>
|
||||||
<Pagination
|
<Pagination
|
||||||
value={page}
|
value={page}
|
||||||
onChange={(newPage) => load(newPage)} // ini penting!
|
onChange={(newPage) => {
|
||||||
|
load(newPage, 10, search);
|
||||||
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
|
}}
|
||||||
total={totalPages}
|
total={totalPages}
|
||||||
mt="md"
|
mt="md"
|
||||||
mb="md"
|
mb="md"
|
||||||
|
color="blue"
|
||||||
|
radius="md"
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleDelete}
|
||||||
|
text="Apakah anda yakin ingin menghapus tarif layanan ini?"
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
import { Context } from "elysia";
|
import { Context } from "elysia";
|
||||||
|
|
||||||
type FasilitasKesehatanInput = {
|
const fasilitasKesehatanCreate = async (context: Context) => {
|
||||||
|
const body = (await context.body) as {
|
||||||
name: string;
|
name: string;
|
||||||
informasiUmum: { fasilitas: string; alamat: string; jamOperasional: string };
|
informasiUmum: { fasilitas: string; alamat: string; jamOperasional: string };
|
||||||
layananUnggulan: { content: string };
|
layananUnggulan: { content: string };
|
||||||
dokterdanTenagaMedis: { name: string; specialist: string; jadwal: string };
|
dokterdanTenagaMedis: string[]; // ← ARRAY OF ID
|
||||||
fasilitasPendukung: { content: string };
|
fasilitasPendukung: { content: string };
|
||||||
prosedurPendaftaran: { content: string };
|
prosedurPendaftaran: { content: string };
|
||||||
tarifDanLayanan: { layanan: string; tarif: string };
|
tarifDanLayanan: string[]; // ← ARRAY OF ID
|
||||||
};
|
};
|
||||||
|
|
||||||
const fasilitasKesehatanCreate = async (context: Context) => {
|
|
||||||
const body = await context.body as FasilitasKesehatanInput;
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
informasiUmum,
|
informasiUmum,
|
||||||
@@ -24,25 +22,30 @@ const fasilitasKesehatanCreate = async (context: Context) => {
|
|||||||
tarifDanLayanan,
|
tarifDanLayanan,
|
||||||
} = body;
|
} = body;
|
||||||
|
|
||||||
// Buat masing-masing relasi terlebih dahulu
|
// CREATE SINGLE DATA
|
||||||
const [createdInformasiUmum, createdLayananUnggulan, createdDokter, createdPendukung, createdProsedur, createdTarif] = await Promise.all([
|
const [createdInformasi, createdUnggulan, createdPendukung, createdProsedur] =
|
||||||
|
await Promise.all([
|
||||||
prisma.informasiUmum.create({ data: informasiUmum }),
|
prisma.informasiUmum.create({ data: informasiUmum }),
|
||||||
prisma.layananUnggulan.create({ data: layananUnggulan }),
|
prisma.layananUnggulan.create({ data: layananUnggulan }),
|
||||||
prisma.dokterdanTenagaMedis.create({ data: dokterdanTenagaMedis }),
|
|
||||||
prisma.fasilitasPendukung.create({ data: fasilitasPendukung }),
|
prisma.fasilitasPendukung.create({ data: fasilitasPendukung }),
|
||||||
prisma.prosedurPendaftaran.create({ data: prosedurPendaftaran }),
|
prisma.prosedurPendaftaran.create({ data: prosedurPendaftaran }),
|
||||||
prisma.tarifDanLayanan.create({ data: tarifDanLayanan }),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// ✅ CUKUP CONNECT KE ID YANG SUDAH ADA
|
||||||
const fasilitas = await prisma.fasilitasKesehatan.create({
|
const fasilitas = await prisma.fasilitasKesehatan.create({
|
||||||
data: {
|
data: {
|
||||||
name,
|
name,
|
||||||
informasiUmumId: createdInformasiUmum.id,
|
informasiUmumId: createdInformasi.id,
|
||||||
layananUnggulanId: createdLayananUnggulan.id,
|
layananUnggulanId: createdUnggulan.id,
|
||||||
dokterdanTenagaMedisId: createdDokter.id,
|
|
||||||
fasilitasPendukungId: createdPendukung.id,
|
fasilitasPendukungId: createdPendukung.id,
|
||||||
prosedurPendaftaranId: createdProsedur.id,
|
prosedurPendaftaranId: createdProsedur.id,
|
||||||
tarifDanLayananId: createdTarif.id,
|
|
||||||
|
dokterdantenagamedis: {
|
||||||
|
connect: dokterdanTenagaMedis.map(id => ({ id })), // ← langsung dari input
|
||||||
|
},
|
||||||
|
tarifdanlayanan: {
|
||||||
|
connect: tarifDanLayanan.map(id => ({ id })), // ← langsung dari input
|
||||||
|
},
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
informasiumum: true,
|
informasiumum: true,
|
||||||
|
|||||||
@@ -4,40 +4,12 @@ import { Context } from "elysia";
|
|||||||
const fasilitasKesehatanDelete = async (context: Context) => {
|
const fasilitasKesehatanDelete = async (context: Context) => {
|
||||||
const id = context.params?.id as string;
|
const id = context.params?.id as string;
|
||||||
|
|
||||||
if (!id) {
|
const data = await prisma.fasilitasKesehatan.findUnique({ where: { id } });
|
||||||
return {
|
if (!data) return { status: 404, message: "Data tidak ditemukan" };
|
||||||
status: 400,
|
|
||||||
message: "ID tidak ditemukan",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fasilitasKesehatan = await prisma.fasilitasKesehatan.findUnique({
|
await prisma.fasilitasKesehatan.delete({ where: { id } });
|
||||||
where: { id },
|
|
||||||
include: {
|
|
||||||
informasiumum: true,
|
|
||||||
layananunggulan: true,
|
|
||||||
dokterdantenagamedis: true,
|
|
||||||
fasilitaspendukung: true,
|
|
||||||
prosedurpendaftaran: true,
|
|
||||||
tarifdanlayanan: true,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!fasilitasKesehatan) {
|
return { success: true, message: "Berhasil dihapus" };
|
||||||
return {
|
};
|
||||||
status: 404,
|
|
||||||
message: "Fasilitas kesehatan tidak ditemukan",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.fasilitasKesehatan.delete({
|
export default fasilitasKesehatanDelete;
|
||||||
where: { id },
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
success: true,
|
|
||||||
message: "Fasilitas kesehatan berhasil dihapus",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default fasilitasKesehatanDelete
|
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ type FormCreate = {
|
|||||||
name: string;
|
name: string;
|
||||||
specialist: string;
|
specialist: string;
|
||||||
jadwal: string;
|
jadwal: string;
|
||||||
|
jadwalLibur: string;
|
||||||
|
jamBukaOperasional: string;
|
||||||
|
jamTutupOperasional: string;
|
||||||
|
jamBukaLibur: string;
|
||||||
|
jamTutupLibur: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function dokterTenagaMedisCreate(context: Context) {
|
export default async function dokterTenagaMedisCreate(context: Context) {
|
||||||
@@ -15,11 +20,21 @@ export default async function dokterTenagaMedisCreate(context: Context) {
|
|||||||
name: body.name,
|
name: body.name,
|
||||||
specialist: body.specialist,
|
specialist: body.specialist,
|
||||||
jadwal: body.jadwal,
|
jadwal: body.jadwal,
|
||||||
|
jadwalLibur: body.jadwalLibur,
|
||||||
|
jamBukaOperasional: body.jamBukaOperasional,
|
||||||
|
jamTutupOperasional: body.jamTutupOperasional,
|
||||||
|
jamBukaLibur: body.jamBukaLibur,
|
||||||
|
jamTutupLibur: body.jamTutupLibur,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
name: true,
|
name: true,
|
||||||
specialist: true,
|
specialist: true,
|
||||||
jadwal: true,
|
jadwal: true,
|
||||||
|
jadwalLibur: true,
|
||||||
|
jamBukaOperasional: true,
|
||||||
|
jamTutupOperasional: true,
|
||||||
|
jamBukaLibur: true,
|
||||||
|
jamTutupLibur: true,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ async function dokterTenagaMedisFindMany(context: Context) {
|
|||||||
{ name: { contains: search, mode: 'insensitive' } },
|
{ name: { contains: search, mode: 'insensitive' } },
|
||||||
{ specialist: { contains: search, mode: 'insensitive' } },
|
{ specialist: { contains: search, mode: 'insensitive' } },
|
||||||
{ jadwal: { contains: search, mode: 'insensitive' } },
|
{ jadwal: { contains: search, mode: 'insensitive' } },
|
||||||
|
{ jadwalLibur: { contains: search, mode: 'insensitive' } },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ const DokterTenagaMedis = new Elysia({
|
|||||||
name: t.String(),
|
name: t.String(),
|
||||||
specialist: t.String(),
|
specialist: t.String(),
|
||||||
jadwal: t.String(),
|
jadwal: t.String(),
|
||||||
|
jadwalLibur: t.String(),
|
||||||
|
jamBukaOperasional: t.String(),
|
||||||
|
jamTutupOperasional: t.String(),
|
||||||
|
jamBukaLibur: t.String(),
|
||||||
|
jamTutupLibur: t.String(),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.put("/:id", dokterTenagaMedisUpdate, {
|
.put("/:id", dokterTenagaMedisUpdate, {
|
||||||
@@ -26,6 +31,11 @@ const DokterTenagaMedis = new Elysia({
|
|||||||
name: t.String(),
|
name: t.String(),
|
||||||
specialist: t.String(),
|
specialist: t.String(),
|
||||||
jadwal: t.String(),
|
jadwal: t.String(),
|
||||||
|
jadwalLibur: t.String(),
|
||||||
|
jamBukaOperasional: t.String(),
|
||||||
|
jamTutupOperasional: t.String(),
|
||||||
|
jamBukaLibur: t.String(),
|
||||||
|
jamTutupLibur: t.String(),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.delete("/del/:id", dokterTenagaMedisDelete)
|
.delete("/del/:id", dokterTenagaMedisDelete)
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ type FormUpdate = {
|
|||||||
name: string;
|
name: string;
|
||||||
specialist: string;
|
specialist: string;
|
||||||
jadwal: string;
|
jadwal: string;
|
||||||
|
jadwalLibur: string;
|
||||||
|
jamBukaOperasional: string;
|
||||||
|
jamTutupOperasional: string;
|
||||||
|
jamBukaLibur: string;
|
||||||
|
jamTutupLibur: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function dokterTenagaMedisUpdate(context: Context) {
|
export default async function dokterTenagaMedisUpdate(context: Context) {
|
||||||
@@ -18,6 +23,12 @@ export default async function dokterTenagaMedisUpdate(context: Context) {
|
|||||||
name: body.name,
|
name: body.name,
|
||||||
specialist: body.specialist,
|
specialist: body.specialist,
|
||||||
jadwal: body.jadwal,
|
jadwal: body.jadwal,
|
||||||
|
jadwalLibur: body.jadwalLibur,
|
||||||
|
jamBukaOperasional: body.jamBukaOperasional,
|
||||||
|
jamTutupOperasional: body.jamTutupOperasional,
|
||||||
|
jamBukaLibur: body.jamBukaLibur,
|
||||||
|
jamTutupLibur: body.jamTutupLibur,
|
||||||
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Elysia, t } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
import fasilitasKesehatanCreate from "./create";
|
import fasilitasKesehatanCreate from "./create";
|
||||||
import findManyFasilitasKesehatan from "./findMany";
|
import fasilitasKesehatanFindMany from "./findMany";
|
||||||
import findUniqueFasilitasKesehatan from "./findUnique";
|
import findUniqueFasilitasKesehatan from "./findUnique";
|
||||||
import fasilitasKesehatanUpdate from "./updt";
|
import fasilitasKesehatanUpdate from "./updt";
|
||||||
import fasilitasKesehatanDelete from "./del";
|
import fasilitasKesehatanDelete from "./del";
|
||||||
@@ -9,42 +9,61 @@ const FasilitasKesehatan = new Elysia({
|
|||||||
prefix: "fasilitas-kesehatan",
|
prefix: "fasilitas-kesehatan",
|
||||||
tags: ["Kesehatan/Fasilitas Kesehatan"],
|
tags: ["Kesehatan/Fasilitas Kesehatan"],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// CREATE
|
||||||
|
// ==========================
|
||||||
.post("/create", fasilitasKesehatanCreate, {
|
.post("/create", fasilitasKesehatanCreate, {
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
name: t.String(),
|
name: t.String(),
|
||||||
|
|
||||||
informasiUmum: t.Object({
|
informasiUmum: t.Object({
|
||||||
fasilitas: t.String(),
|
fasilitas: t.String(),
|
||||||
alamat: t.String(),
|
alamat: t.String(),
|
||||||
jamOperasional: t.String(),
|
jamOperasional: t.String(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
layananUnggulan: t.Object({
|
layananUnggulan: t.Object({
|
||||||
content: t.String(),
|
content: t.String(),
|
||||||
}),
|
}),
|
||||||
dokterdanTenagaMedis: t.Object({
|
|
||||||
name: t.String(),
|
dokterdanTenagaMedis: t.Array(t.String()), // FIX karena create pakai array of string
|
||||||
specialist: t.String(),
|
|
||||||
jadwal: t.String(),
|
|
||||||
}),
|
|
||||||
fasilitasPendukung: t.Object({
|
fasilitasPendukung: t.Object({
|
||||||
content: t.String(),
|
content: t.String(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
prosedurPendaftaran: t.Object({
|
prosedurPendaftaran: t.Object({
|
||||||
content: t.String(),
|
content: t.String(),
|
||||||
}),
|
}),
|
||||||
tarifDanLayanan: t.Object({
|
|
||||||
layanan: t.String(),
|
tarifDanLayanan: t.Array(t.String()), // FIX karena create pakai array of string
|
||||||
tarif: t.String(),
|
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.get("/find-many", findManyFasilitasKesehatan)
|
|
||||||
|
// ==========================
|
||||||
|
// FIND MANY
|
||||||
|
// ==========================
|
||||||
|
.get("/find-many", fasilitasKesehatanFindMany)
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// DELETE
|
||||||
|
// ==========================
|
||||||
.delete("/del/:id", fasilitasKesehatanDelete)
|
.delete("/del/:id", fasilitasKesehatanDelete)
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// FIND UNIQUE
|
||||||
|
// ==========================
|
||||||
.get("/:id", async (context) => {
|
.get("/:id", async (context) => {
|
||||||
const response = await findUniqueFasilitasKesehatan(
|
const response = await findUniqueFasilitasKesehatan(
|
||||||
new Request(context.request)
|
new Request(context.request)
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// UPDATE
|
||||||
|
// ==========================
|
||||||
.put(
|
.put(
|
||||||
"/:id",
|
"/:id",
|
||||||
async (context) => {
|
async (context) => {
|
||||||
@@ -54,29 +73,30 @@ const FasilitasKesehatan = new Elysia({
|
|||||||
{
|
{
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
name: t.String(),
|
name: t.String(),
|
||||||
|
|
||||||
informasiUmum: t.Object({
|
informasiUmum: t.Object({
|
||||||
fasilitas: t.String(),
|
fasilitas: t.String(),
|
||||||
alamat: t.String(),
|
alamat: t.String(),
|
||||||
jamOperasional: t.String(),
|
jamOperasional: t.String(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
layananUnggulan: t.Object({
|
layananUnggulan: t.Object({
|
||||||
content: t.String(),
|
content: t.String(),
|
||||||
}),
|
}),
|
||||||
dokterdanTenagaMedis: t.Object({
|
|
||||||
name: t.String(),
|
// FIX → harus array of string (ID dokter)
|
||||||
specialist: t.String(),
|
dokterdanTenagaMedis: t.Array(t.String()),
|
||||||
jadwal: t.String(),
|
|
||||||
}),
|
|
||||||
fasilitasPendukung: t.Object({
|
fasilitasPendukung: t.Object({
|
||||||
content: t.String(),
|
content: t.String(),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
prosedurPendaftaran: t.Object({
|
prosedurPendaftaran: t.Object({
|
||||||
content: t.String(),
|
content: t.String(),
|
||||||
}),
|
}),
|
||||||
tarifDanLayanan: t.Object({
|
|
||||||
layanan: t.String(),
|
// FIX → harus array of string (ID tarif)
|
||||||
tarif: t.String(),
|
tarifDanLayanan: t.Array(t.String()),
|
||||||
}),
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
type FormCreate = {
|
||||||
|
layanan: string;
|
||||||
|
tarif: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function tarifLayananCreate(context: Context) {
|
||||||
|
const body = context.body as FormCreate;
|
||||||
|
|
||||||
|
const created = await prisma.tarifDanLayanan.create({
|
||||||
|
data: {
|
||||||
|
layanan: body.layanan,
|
||||||
|
tarif: body.tarif,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
layanan: true,
|
||||||
|
tarif: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Sukses menambahkan dokter tenaga medis",
|
||||||
|
data: created,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
export default async function tarifLayananDelete(context: Context) {
|
||||||
|
|
||||||
|
const id = context.params?.id;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak ditemukan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = await prisma.tarifDanLayanan.findUnique({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existing) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Data tidak ditemukan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleted = await prisma.tarifDanLayanan.delete({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Data berhasil dihapus",
|
||||||
|
data: deleted,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
// /api/berita/findManyPaginated.ts
|
||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
async function tarifLayananFindMany(context: Context) {
|
||||||
|
// Ambil parameter dari query
|
||||||
|
const page = Number(context.query.page) || 1;
|
||||||
|
const limit = Number(context.query.limit) || 10;
|
||||||
|
const search = (context.query.search as string) || '';
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
|
||||||
|
// Buat where clause
|
||||||
|
const where: any = { isActive: true };
|
||||||
|
|
||||||
|
// Tambahkan pencarian (jika ada)
|
||||||
|
if (search) {
|
||||||
|
where.OR = [
|
||||||
|
{ layanan: { contains: search, mode: 'insensitive' } },
|
||||||
|
{ tarif: { contains: search, mode: 'insensitive' } },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Ambil data dan total count secara paralel
|
||||||
|
const [data, total] = await Promise.all([
|
||||||
|
prisma.tarifDanLayanan.findMany({
|
||||||
|
where,
|
||||||
|
skip,
|
||||||
|
take: limit,
|
||||||
|
orderBy: { layanan: 'asc' },
|
||||||
|
}),
|
||||||
|
prisma.tarifDanLayanan.count({ where }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Berhasil ambil data tarif layanan dengan pagination",
|
||||||
|
data,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
total,
|
||||||
|
totalPages: Math.ceil(total / limit),
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error di findMany paginated:", e);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Gagal mengambil data tarif layanan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default tarifLayananFindMany;
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
|
export default async function tarifLayananFindUnique(request: Request) {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const pathSegments = url.pathname.split('/');
|
||||||
|
|
||||||
|
const id = pathSegments[pathSegments.length - 1];
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: 'ID tidak boleh kosong',
|
||||||
|
}, {status: 400})
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typeof id !== 'string') {
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "ID tidak valid",
|
||||||
|
}, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await prisma.tarifDanLayanan.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "Data tidak ditemukan",
|
||||||
|
}, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
success: true,
|
||||||
|
message: "Berhasil mengambil data berdasarkan ID",
|
||||||
|
data,
|
||||||
|
}, { status: 200 });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching data:", error);
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "Terjadi kesalahan saat mengambil data",
|
||||||
|
}, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import Elysia, { t } from "elysia";
|
||||||
|
import tarifLayananCreate from "./create";
|
||||||
|
import tarifLayananFindMany from "./findMany";
|
||||||
|
import tarifLayananFindUnique from "./findUnique";
|
||||||
|
import tarifLayananDelete from "./del";
|
||||||
|
import tarifLayananUpdate from "./updt";
|
||||||
|
|
||||||
|
const TarifLayanan = new Elysia({
|
||||||
|
prefix: "/tarifdanlayanan",
|
||||||
|
tags: ["Data Kesehatan/Fasilitas Kesehatan/Tarif Layanan"]
|
||||||
|
})
|
||||||
|
.get("/:id", async (context) => {
|
||||||
|
const response = await tarifLayananFindUnique(new Request(context.request));
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.get("/findMany", tarifLayananFindMany)
|
||||||
|
.post("/create", tarifLayananCreate, {
|
||||||
|
body: t.Object({
|
||||||
|
tarif: t.String(),
|
||||||
|
layanan: t.String()
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.put("/:id", tarifLayananUpdate, {
|
||||||
|
body: t.Object({
|
||||||
|
tarif: t.String(),
|
||||||
|
layanan: t.String(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.delete("/del/:id", tarifLayananDelete)
|
||||||
|
export default TarifLayanan
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
|
||||||
|
type FormUpdate = {
|
||||||
|
layanan: string;
|
||||||
|
tarif: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function tarifLayananUpdate(context: Context) {
|
||||||
|
const body = (await context.body) as FormUpdate;
|
||||||
|
const id = context.params.id as string;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await prisma.tarifDanLayanan.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
layanan: body.layanan,
|
||||||
|
tarif: body.tarif,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Berhasil mengupdate data tarif layanan",
|
||||||
|
data: result,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating data tarif layanan:", error);
|
||||||
|
throw new Error(
|
||||||
|
"Gagal mengupdate data tarif layanan: " + (error as Error).message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,32 +5,26 @@ type FasilitasKesehatanInput = {
|
|||||||
name: string;
|
name: string;
|
||||||
informasiUmum: { fasilitas: string; alamat: string; jamOperasional: string };
|
informasiUmum: { fasilitas: string; alamat: string; jamOperasional: string };
|
||||||
layananUnggulan: { content: string };
|
layananUnggulan: { content: string };
|
||||||
dokterdanTenagaMedis: { name: string; specialist: string; jadwal: string };
|
dokterdanTenagaMedis: string[]; // ← ID saja
|
||||||
fasilitasPendukung: { content: string };
|
fasilitasPendukung: { content: string };
|
||||||
prosedurPendaftaran: { content: string };
|
prosedurPendaftaran: { content: string };
|
||||||
tarifDanLayanan: { layanan: string; tarif: string };
|
tarifDanLayanan: string[]; // ← ID saja
|
||||||
};
|
};
|
||||||
|
|
||||||
const fasilitasKesehatanUpdate = async (context: Context) => {
|
const fasilitasKesehatanUpdate = async (context: Context) => {
|
||||||
const id = context.params?.id as string;
|
const id = context.params?.id as string;
|
||||||
const body = await context.body as FasilitasKesehatanInput;
|
const body = (await context.body) as FasilitasKesehatanInput;
|
||||||
|
|
||||||
if (!id) {
|
|
||||||
return new Response(
|
|
||||||
JSON.stringify({ success: false, message: "ID is required" }),
|
|
||||||
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const existing = await prisma.fasilitasKesehatan.findUnique({
|
const existing = await prisma.fasilitasKesehatan.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
|
include: {
|
||||||
|
dokterdantenagamedis: true,
|
||||||
|
tarifdanlayanan: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
return new Response(
|
return { success: false, message: "Data tidak ditemukan" };
|
||||||
JSON.stringify({ success: false, message: "Data not found" }),
|
|
||||||
{ status: 404, headers: { "Content-Type": "application/json" } }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -43,38 +37,46 @@ const fasilitasKesehatanUpdate = async (context: Context) => {
|
|||||||
tarifDanLayanan,
|
tarifDanLayanan,
|
||||||
} = body;
|
} = body;
|
||||||
|
|
||||||
// Update data masing-masing relasi
|
// update relasi 1-1
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
prisma.informasiUmum.update({
|
prisma.informasiUmum.update({
|
||||||
where: { id: existing.informasiUmumId },
|
where: { id: existing.informasiUmumId },
|
||||||
data: informasiUmum,
|
data: informasiUmum,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
prisma.layananUnggulan.update({
|
prisma.layananUnggulan.update({
|
||||||
where: { id: existing.layananUnggulanId },
|
where: { id: existing.layananUnggulanId },
|
||||||
data: layananUnggulan,
|
data: layananUnggulan,
|
||||||
}),
|
}),
|
||||||
prisma.dokterdanTenagaMedis.update({
|
|
||||||
where: { id: existing.dokterdanTenagaMedisId },
|
|
||||||
data: dokterdanTenagaMedis,
|
|
||||||
}),
|
|
||||||
prisma.fasilitasPendukung.update({
|
prisma.fasilitasPendukung.update({
|
||||||
where: { id: existing.fasilitasPendukungId },
|
where: { id: existing.fasilitasPendukungId },
|
||||||
data: fasilitasPendukung,
|
data: fasilitasPendukung,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
prisma.prosedurPendaftaran.update({
|
prisma.prosedurPendaftaran.update({
|
||||||
where: { id: existing.prosedurPendaftaranId },
|
where: { id: existing.prosedurPendaftaranId },
|
||||||
data: prosedurPendaftaran,
|
data: prosedurPendaftaran,
|
||||||
}),
|
}),
|
||||||
prisma.tarifDanLayanan.update({
|
|
||||||
where: { id: existing.tarifDanLayananId },
|
|
||||||
data: tarifDanLayanan,
|
|
||||||
}),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Update main record
|
// update m2m
|
||||||
const updated = await prisma.fasilitasKesehatan.update({
|
const updated = await prisma.fasilitasKesehatan.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { name },
|
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
|
||||||
|
// reset dokter lama → ganti baru
|
||||||
|
dokterdantenagamedis: {
|
||||||
|
set: dokterdanTenagaMedis.map((id) => ({ id })),
|
||||||
|
},
|
||||||
|
|
||||||
|
tarifdanlayanan: {
|
||||||
|
set: tarifDanLayanan.map((id) => ({ id })),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
include: {
|
include: {
|
||||||
informasiumum: true,
|
informasiumum: true,
|
||||||
layananunggulan: true,
|
layananunggulan: true,
|
||||||
@@ -87,7 +89,7 @@ const fasilitasKesehatanUpdate = async (context: Context) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: "Fasilitas berhasil diupdate",
|
message: "Fasilitas diupdate",
|
||||||
data: updated,
|
data: updated,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import Kelahiran from "./data_kesehatan_warga/persentase_kelahiran_kematian/kela
|
|||||||
import Kematian from "./data_kesehatan_warga/persentase_kelahiran_kematian/kematian";
|
import Kematian from "./data_kesehatan_warga/persentase_kelahiran_kematian/kematian";
|
||||||
import DokterTenagaMedis from "./data_kesehatan_warga/fasilitas_kesehatan/dokter-tenaga-medis";
|
import DokterTenagaMedis from "./data_kesehatan_warga/fasilitas_kesehatan/dokter-tenaga-medis";
|
||||||
import PendaftaranJadwalKegiatan from "./data_kesehatan_warga/jadwal_kegiatan/pendaftaran";
|
import PendaftaranJadwalKegiatan from "./data_kesehatan_warga/jadwal_kegiatan/pendaftaran";
|
||||||
|
import TarifLayanan from "./data_kesehatan_warga/fasilitas_kesehatan/tarif-layanan";
|
||||||
|
|
||||||
|
|
||||||
const Kesehatan = new Elysia({
|
const Kesehatan = new Elysia({
|
||||||
@@ -46,5 +47,6 @@ const Kesehatan = new Elysia({
|
|||||||
.use(Kelahiran)
|
.use(Kelahiran)
|
||||||
.use(Kematian)
|
.use(Kematian)
|
||||||
.use(DokterTenagaMedis)
|
.use(DokterTenagaMedis)
|
||||||
|
.use(TarifLayanan)
|
||||||
.use(PendaftaranJadwalKegiatan)
|
.use(PendaftaranJadwalKegiatan)
|
||||||
export default Kesehatan;
|
export default Kesehatan;
|
||||||
|
|||||||
@@ -557,25 +557,37 @@ export default async function searchFindMany(context: Context) {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
layananunggulan: { content: { contains: query, mode: "insensitive" } },
|
layananunggulan: { content: { contains: query, mode: "insensitive" } },
|
||||||
dokterdantenagamedis: {
|
|
||||||
OR: [
|
|
||||||
{ name: { contains: query, mode: "insensitive" } },
|
|
||||||
{ specialist: { contains: query, mode: "insensitive" } },
|
|
||||||
{ jadwal: { contains: query, mode: "insensitive" } },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
fasilitaspendukung: {
|
fasilitaspendukung: {
|
||||||
content: { contains: query, mode: "insensitive" },
|
content: { contains: query, mode: "insensitive" },
|
||||||
},
|
},
|
||||||
prosedurpendaftaran: {
|
prosedurpendaftaran: {
|
||||||
content: { contains: query, mode: "insensitive" },
|
content: { contains: query, mode: "insensitive" },
|
||||||
},
|
},
|
||||||
tarifdanlayanan: {
|
|
||||||
OR: [
|
|
||||||
{ layanan: { contains: query, mode: "insensitive" } },
|
|
||||||
{ tarif: { contains: query, mode: "insensitive" } },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "doktertenagamedis") {
|
||||||
|
const data = await prisma.dokterdanTenagaMedis.findMany({
|
||||||
|
where: {
|
||||||
|
name: { contains: query, mode: "insensitive" },
|
||||||
|
specialist: { contains: query, mode: "insensitive" },
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "tarifdanlayanan") {
|
||||||
|
const data = await prisma.tarifDanLayanan.findMany({
|
||||||
|
where: {
|
||||||
|
layanan: { contains: query, mode: "insensitive" },
|
||||||
|
tarif: { contains: query, mode: "insensitive" },
|
||||||
},
|
},
|
||||||
skip,
|
skip,
|
||||||
take: limitNum,
|
take: limitNum,
|
||||||
@@ -1567,6 +1579,8 @@ export default async function searchFindMany(context: Context) {
|
|||||||
jenisProgramYangDiselenggarakan,
|
jenisProgramYangDiselenggarakan,
|
||||||
dataPerpustakaan,
|
dataPerpustakaan,
|
||||||
dataPendidikan,
|
dataPendidikan,
|
||||||
|
dokterDanTenagaMedis,
|
||||||
|
tarifDanLayanan
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
prisma.pejabatDesa.findMany({
|
prisma.pejabatDesa.findMany({
|
||||||
where: { name: { contains: query, mode: "insensitive" } },
|
where: { name: { contains: query, mode: "insensitive" } },
|
||||||
@@ -1894,25 +1908,27 @@ export default async function searchFindMany(context: Context) {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
layananunggulan: { content: { contains: query, mode: "insensitive" } },
|
layananunggulan: { content: { contains: query, mode: "insensitive" } },
|
||||||
dokterdantenagamedis: {
|
|
||||||
OR: [
|
|
||||||
{ name: { contains: query, mode: "insensitive" } },
|
|
||||||
{ specialist: { contains: query, mode: "insensitive" } },
|
|
||||||
{ jadwal: { contains: query, mode: "insensitive" } },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
fasilitaspendukung: {
|
fasilitaspendukung: {
|
||||||
content: { contains: query, mode: "insensitive" },
|
content: { contains: query, mode: "insensitive" },
|
||||||
},
|
},
|
||||||
prosedurpendaftaran: {
|
prosedurpendaftaran: {
|
||||||
content: { contains: query, mode: "insensitive" },
|
content: { contains: query, mode: "insensitive" },
|
||||||
},
|
},
|
||||||
tarifdanlayanan: {
|
|
||||||
OR: [
|
|
||||||
{ layanan: { contains: query, mode: "insensitive" } },
|
|
||||||
{ tarif: { contains: query, mode: "insensitive" } },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.dokterdanTenagaMedis.findMany({
|
||||||
|
where: {
|
||||||
|
name: { contains: query, mode: "insensitive" },
|
||||||
|
specialist: { contains: query, mode: "insensitive" },
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.tarifDanLayanan.findMany({
|
||||||
|
where: {
|
||||||
|
tarif: { contains: query, mode: "insensitive" },
|
||||||
|
layanan: { contains: query, mode: "insensitive" },
|
||||||
},
|
},
|
||||||
take: limitNum,
|
take: limitNum,
|
||||||
}),
|
}),
|
||||||
@@ -2559,6 +2575,8 @@ export default async function searchFindMany(context: Context) {
|
|||||||
...penghargaan.map((b) => ({ type: "penghargaan", ...b })),
|
...penghargaan.map((b) => ({ type: "penghargaan", ...b })),
|
||||||
...posyandu.map((b) => ({ type: "posyandu", ...b })),
|
...posyandu.map((b) => ({ type: "posyandu", ...b })),
|
||||||
...fasilitasKesehatan.map((b) => ({ type: "fasilitasKesehatan", ...b })),
|
...fasilitasKesehatan.map((b) => ({ type: "fasilitasKesehatan", ...b })),
|
||||||
|
...dokterDanTenagaMedis.map((b) => ({ type: "dokterdanTenagaMedis", ...b })),
|
||||||
|
...tarifDanLayanan.map((b) => ({ type: "tarifDanLayanan", ...b })),
|
||||||
...jadwalKegiatan.map((b) => ({ type: "jadwalKegiatan", ...b })),
|
...jadwalKegiatan.map((b) => ({ type: "jadwalKegiatan", ...b })),
|
||||||
...artikelKesehatan.map((b) => ({ type: "artikelKesehatan", ...b })),
|
...artikelKesehatan.map((b) => ({ type: "artikelKesehatan", ...b })),
|
||||||
...puskesmas.map((b) => ({ type: "puskesmas", ...b })),
|
...puskesmas.map((b) => ({ type: "puskesmas", ...b })),
|
||||||
|
|||||||
@@ -42,9 +42,7 @@ function Page() {
|
|||||||
const alamat = data?.informasiumum?.alamat || '-';
|
const alamat = data?.informasiumum?.alamat || '-';
|
||||||
const jam = data?.informasiumum?.jamOperasional || '-';
|
const jam = data?.informasiumum?.jamOperasional || '-';
|
||||||
const layananUnggulan = data?.layananunggulan?.content || '';
|
const layananUnggulan = data?.layananunggulan?.content || '';
|
||||||
const tenaga = data?.dokterdantenagamedis || null;
|
|
||||||
const fasilitasPendukungHtml = data?.fasilitaspendukung?.content || '';
|
const fasilitasPendukungHtml = data?.fasilitaspendukung?.content || '';
|
||||||
const tarif = (data?.tarifdanlayanan as TarifDanLayanan) || null;
|
|
||||||
const kontak = (data?.kontak as Kontak) || {
|
const kontak = (data?.kontak as Kontak) || {
|
||||||
telepon: '(0361) 123456',
|
telepon: '(0361) 123456',
|
||||||
whatsapp: '6289647037426',
|
whatsapp: '6289647037426',
|
||||||
@@ -211,7 +209,7 @@ function Page() {
|
|||||||
<Card radius="xl" p="lg" withBorder>
|
<Card radius="xl" p="lg" withBorder>
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Title order={4}>Dokter & Tenaga Medis</Title>
|
<Title order={4}>Dokter & Tenaga Medis</Title>
|
||||||
<Table highlightOnHover withTableBorder withColumnBorders stickyHeader stickyHeaderOffset={0} aria-label="Tabel Dokter">
|
<Table highlightOnHover withTableBorder withColumnBorders aria-label="Tabel Dokter">
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh>Nama</TableTh>
|
<TableTh>Nama</TableTh>
|
||||||
@@ -220,12 +218,19 @@ function Page() {
|
|||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
{tenaga ? (
|
{Array.isArray(data?.dokterdantenagamedis) && data.dokterdantenagamedis.length > 0 ? (
|
||||||
<TableTr>
|
data.dokterdantenagamedis.map((dokter: any) => (
|
||||||
<TableTd><Group gap="xs"><IconUser size={16} /><Text>{tenaga?.name || '-'}</Text></Group></TableTd>
|
<TableTr key={dokter.id}>
|
||||||
<TableTd>{tenaga?.specialist || '-'}</TableTd>
|
<TableTd>
|
||||||
<TableTd>{tenaga?.jadwal || '-'}</TableTd>
|
<Group gap="xs">
|
||||||
|
<IconUser size={16} />
|
||||||
|
<Text>{dokter.name || '-'}</Text>
|
||||||
|
</Group>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd>{dokter.specialist || '-'}</TableTd>
|
||||||
|
<TableTd>{dokter.jadwal || '-'}</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
|
))
|
||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={3}>
|
<TableTd colSpan={3}>
|
||||||
@@ -270,11 +275,13 @@ function Page() {
|
|||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
{tarif ? (
|
{Array.isArray(data?.tarifdanlayanan) && data.tarifdanlayanan.length > 0 ? (
|
||||||
<TableTr>
|
data.tarifdanlayanan.map((item: any) => (
|
||||||
<TableTd>{tarif?.layanan || '-'}</TableTd>
|
<TableTr key={item.id}>
|
||||||
<TableTd>{formatRupiah(tarif?.tarif)}</TableTd>
|
<TableTd>{item.layanan || '-'}</TableTd>
|
||||||
|
<TableTd>{formatRupiah(item.tarif)}</TableTd>
|
||||||
</TableTr>
|
</TableTr>
|
||||||
|
))
|
||||||
) : (
|
) : (
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTd colSpan={2}>
|
<TableTd colSpan={2}>
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ function SosmedView({
|
|||||||
loading="lazy"
|
loading="lazy"
|
||||||
src={src}
|
src={src}
|
||||||
alt={item.name}
|
alt={item.name}
|
||||||
fit={item.image?.link ? "cover" : "contain"}
|
w={24}
|
||||||
|
h={24}
|
||||||
|
fit="contain"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ const getDetailUrl = (item: { type?: string; id: string | number; [key: string]:
|
|||||||
penghargaan: () => '/darmasaba/desa/penghargaan',
|
penghargaan: () => '/darmasaba/desa/penghargaan',
|
||||||
posyandu: (id) => `/darmasaba/kesehatan/posyandu/${id}`,
|
posyandu: (id) => `/darmasaba/kesehatan/posyandu/${id}`,
|
||||||
fasilitasKesehatan: () => '/darmasaba/kesehatan/data-kesehatan-warga',
|
fasilitasKesehatan: () => '/darmasaba/kesehatan/data-kesehatan-warga',
|
||||||
|
dokterDanTenagaMedis: () => '/darmasaba/kesehatan/data-kesehatan-warga',
|
||||||
|
tarifDanLayanan: () => '/darmasaba/kesehatan/data-kesehatan-warga',
|
||||||
jadwalKegiatan: () => '/darmasaba/kesehatan/data-kesehatan-warga',
|
jadwalKegiatan: () => '/darmasaba/kesehatan/data-kesehatan-warga',
|
||||||
artikelKesehatan: () => '/darmasaba/kesehatan/data-kesehatan-warga',
|
artikelKesehatan: () => '/darmasaba/kesehatan/data-kesehatan-warga',
|
||||||
puskesmas: () => '/darmasaba/kesehatan/puskesmas',
|
puskesmas: () => '/darmasaba/kesehatan/puskesmas',
|
||||||
|
|||||||
Reference in New Issue
Block a user