Menambahkan menu dokter dan tenaga medis, admin bisa create, edit, delet dokter
Menambahkan menu tarif dan layanan, admin bisa create, edit, delete tarif dan layanan Dibagian fasilitas kesehatan admin bisa multiselect bagian dokter dan tarif layanan Di tampilan user juga sudah disesuaikan dengan datanya bisa muncul lebih dari 1 dokter dan 1 tarif layanan
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