diff --git a/prisma/data/ekonomi/struktur-organisasi/hubungan-organisasi.json b/prisma/data/ekonomi/struktur-organisasi/hubungan-organisasi.json deleted file mode 100644 index 0f0e8271..00000000 --- a/prisma/data/ekonomi/struktur-organisasi/hubungan-organisasi.json +++ /dev/null @@ -1,8 +0,0 @@ -[ - { - "id": "650e8400-e29b-41d4-a716-446655440001", - "atasanId": "550e8400-e29b-41d4-a716-446655440001", - "bawahanId": "550e8400-e29b-41d4-a716-446655440002", - "tipe": "Langsung Melapor" - } -] diff --git a/prisma/data/ekonomi/struktur-organisasi/pegawai-bumdes.json b/prisma/data/ekonomi/struktur-organisasi/pegawai-bumdes.json new file mode 100644 index 00000000..0976a812 --- /dev/null +++ b/prisma/data/ekonomi/struktur-organisasi/pegawai-bumdes.json @@ -0,0 +1,91 @@ +[ + { + "id": "cmgewz4gt000704ib91i3f169", + "namaLengkap": "Ida Bagus Surya Prabhawa Manuaba, S.H.,M.H., NL.P.", + "gelarAkademik": "S.H.,M.H.,NL.P.", + "tanggalMasuk": "2020-01-01T00:00:00.000Z", + "email": "bagus@desa.id", + "telepon": "081234567891", + "alamat": "Jl. Raya Desa No. 1", + "posisiId": "kepala_desa", + "isActive": true + }, + { + "id": "cmgewxfvw000004ibee5013f4", + "namaLengkap": "I Ketut Suwanta", + "gelarAkademik": "S.Pt", + "tanggalMasuk": "2020-02-01T00:00:00.000Z", + "email": "suwanta@desa.id", + "telepon": "081234567892", + "alamat": "Jl. Raya Desa No. 2", + "posisiId": "sekretaris_desa", + "isActive": true + }, + { + "id": "cmgewxvqw000104ibgm5l8fzs", + "namaLengkap": "Ni Wayan Supardiati", + "gelarAkademik": "S.Pd", + "tanggalMasuk": "2020-02-01T00:00:00.000Z", + "email": "supardiati@desa.id", + "telepon": "081234567892", + "alamat": "Jl. Raya Desa No. 2", + "posisiId": "kaur_keuangan", + "isActive": true + }, + { + "id": "cmgewy1g9000204ib2n7hbx0i", + "namaLengkap": "I Wayan Agus Juni Artha Saputra", + "gelarAkademik": "S.T.", + "tanggalMasuk": "2020-02-01T00:00:00.000Z", + "email": "agus@desa.id", + "telepon": "081234567892", + "alamat": "Jl. Raya Desa No. 2", + "posisiId": "kadus_banjar_dinas_menesa", + "isActive": true + }, + { + "id": "cmgewybah000304ibgqhn1gm2", + "namaLengkap": "I Wayan Sueca", + "gelarAkademik": "S.H.", + "tanggalMasuk": "2020-02-01T00:00:00.000Z", + "email": "sueca@desa.id", + "telepon": "081234567893", + "alamat": "Jl. Raya Desa No. 2", + "posisiId": "kadus_banjar_dinas_darmasaba", + "isActive": true + }, + { + "id": "cmgewygqz000404ib20sv8nvg", + "namaLengkap": "Si Gede Ketut Astawa", + "gelarAkademik": "S.T.", + "tanggalMasuk": "2020-02-01T00:00:00.000Z", + "email": "astawa@desa.id", + "telepon": "081234567893", + "alamat": "Jl. Raya Desa No. 2", + "posisiId": "kadus_banjar_dinas_bucu", + "isActive": true + }, + { + "id": "cmgewyos1000504ibcu8o2gyk", + "namaLengkap": "I Kadek Arya Minarta", + "gelarAkademik": "S.T.", + "tanggalMasuk": "2020-02-01T00:00:00.000Z", + "email": "minarta@desa.id", + "telepon": "081234567893", + "alamat": "Jl. Raya Desa No. 2", + "posisiId": "kadus_banjar_dinas_gulingan", + "isActive": true + }, + { + "id": "cmgewyxk7000604ib8djs3i6c", + "namaLengkap": "I Gede Andika Pradnya Diputra", + "gelarAkademik": "S.E.", + "tanggalMasuk": "2020-02-01T00:00:00.000Z", + "email": "diputra@desa.id", + "telepon": "081234567893", + "alamat": "Jl. Raya Desa No. 2", + "posisiId": "kadus_banjar_dinas_taman", + "isActive": true + } + +] \ No newline at end of file diff --git a/prisma/data/ekonomi/struktur-organisasi/pegawai.json b/prisma/data/ekonomi/struktur-organisasi/pegawai.json deleted file mode 100644 index 9d7a6437..00000000 --- a/prisma/data/ekonomi/struktur-organisasi/pegawai.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - { - "id": "550e8400-e29b-41d4-a716-446655440001", - "namaLengkap": "Budi Santoso", - "gelarAkademik": "S.IP", - "tanggalMasuk": "2020-01-01T00:00:00.000Z", - "email": "budi@desa.id", - "telepon": "081234567891", - "alamat": "Jl. Raya Desa No. 1", - "posisiId": "kepala_desa", - "isActive": true - }, - { - "id": "550e8400-e29b-41d4-a716-446655440002", - "namaLengkap": "Ani Lestari", - "gelarAkademik": "S.Pd", - "tanggalMasuk": "2020-02-01T00:00:00.000Z", - "email": "ani@desa.id", - "telepon": "081234567892", - "alamat": "Jl. Raya Desa No. 2", - "posisiId": "sekretaris_desa", - "isActive": true - } - ] \ No newline at end of file diff --git a/prisma/data/ekonomi/struktur-organisasi/posisi-organisasi-bumdes.json b/prisma/data/ekonomi/struktur-organisasi/posisi-organisasi-bumdes.json new file mode 100644 index 00000000..4a1699d7 --- /dev/null +++ b/prisma/data/ekonomi/struktur-organisasi/posisi-organisasi-bumdes.json @@ -0,0 +1,159 @@ +[ + [ + { + "id": "kepala_desa", + "nama": "Kepala Desa", + "deskripsi": "Pemimpin desa Darmasaba", + "hierarki": 1, + "parentId": null + }, + { + "id": "kepala_urusan", + "nama": "Kepala Urusan", + "deskripsi": "Pemimpin urusan desa Darmasaba", + "hierarki": 2, + "parentId": "kepala_desa" + }, + { + "id": "sekretaris_desa", + "nama": "Sekretaris Desa", + "deskripsi": "Pengelola administrasi desa", + "hierarki": 2, + "parentId": "kepala_desa" + }, + { + "id": "kaur_keuangan", + "nama": "Kaur Keuangan", + "deskripsi": "Pengelola keuangan desa", + "hierarki": 3, + "parentId": "kaur_umum" + }, + { + "id": "kaur_perencanaan", + "nama": "Kaur Perencanaan", + "deskripsi": "Penyusun program kerja desa", + "hierarki": 3, + "parentId": "kaur_umum" + }, + { + "id": "kaur_umum", + "nama": "Kaur Umum & TU", + "deskripsi": "Pelayanan umum dan administrasi", + "hierarki": 2, + "parentId": "kepala_desa" + }, + { + "id": "kasi_pemerintahan", + "nama": "Kasi Pemerintahan", + "deskripsi": "Urusan pemerintahan dan keamanan", + "hierarki": 2, + "parentId": "kepala_desa" + }, + { + "id": "kasi_pelayanan", + "nama": "Kasi Pelayanan", + "deskripsi": "Urusan pelayanan masyarakat", + "hierarki": 2, + "parentId": "kepala_desa" + }, + { + "id": "kasi_kesejahteraan", + "nama": "Kasi Kesejahteraan", + "deskripsi": "Urusan sosial dan kesejahteraan", + "hierarki": 2, + "parentId": "kepala_desa" + }, + { + "id": "kadus_banjar_dinas_cabe", + "nama": "Kepala Dusun Banjar Dinas Cabe", + "deskripsi": "Pimpinan wilayah Banjar Dinas Cabe", + "hierarki": 3, + "parentId": "sekretaris_desa" + }, + { + "id": "kadus_banjar_dinas_menesa", + "nama": "Kepala Dusun Banjar Dinas Menesa", + "deskripsi": "Pimpinan wilayah Banjar Menesa", + "hierarki": 3, + "parentId": "sekretaris_desa" + }, + { + "id": "kadus_banjar_dinas_penenjoan", + "nama": "Kepala Dusun Banjar Dinas Penenjoan", + "deskripsi": "Pimpinan wilayah Banjar Dinas Penenjoan", + "hierarki": 3, + "parentId": "sekretaris_desa" + }, + { + "id": "kadus_banjar_dinas_telanga", + "nama": "Kepala Dusun Banjar Dinas Telanga", + "deskripsi": "Pimpinan wilayah Banjar Dinas Telanga", + "hierarki": 3, + "parentId": "sekretaris_desa" + }, + { + "id": "kadus_banjar_dinas_tengah", + "nama": "Kepala Dusun Banjar Dinas Tengah", + "deskripsi": "Pimpinan wilayah Banjar Dinas Tengah", + "hierarki": 3, + "parentId": "sekretaris_desa" + }, + { + "id": "kadus_banjar_dinas_baler_pasar", + "nama": "Kepala Dusun Banjar Dinas Baler Pasar", + "deskripsi": "Pimpinan wilayah Banjar Dinas Baler Pasar", + "hierarki": 3, + "parentId": "sekretaris_desa" + }, + { + "id": "kadus_banjar_dinas_bucu", + "nama": "Kepala Dusun Banjar Dinas Bucu", + "deskripsi": "Pimpinan wilayah Banjar Dinas Bucu", + "hierarki": 3, + "parentId": "sekretaris_desa" + }, + { + "id": "kadus_banjar_dinas_gulingan", + "nama": "Kepala Dusun Banjar Dinas Gulingan", + "deskripsi": "Pimpinan wilayah Banjar Dinas Gulingan", + "hierarki": 3, + "parentId": "sekretaris_desa" + }, + { + "id": "kadus_banjar_dinas_bersih", + "nama": "Kepala Dusun Banjar Dinas Bersih", + "deskripsi": "Pimpinan wilayah Banjar Dinas Bersih", + "hierarki": 3, + "parentId": "sekretaris_desa" + }, + { + "id": "kadus_banjar_dinas_umahanyar", + "nama": "Kepala Dusun Banjar Dinas Umahanyar", + "deskripsi": "Pimpinan wilayah Banjar Dinas Umahanyar", + "hierarki": 3, + "parentId": "sekretaris_desa" + }, + { + "id": "kadus_banjar_dinas_taman", + "nama": "Kepala Dusun Banjar Dinas Taman", + "deskripsi": "Pimpinan wilayah Banjar Dinas Taman", + "hierarki": 3, + "parentId": "sekretaris_desa" + }, + { + "id": "kadus_banjar_dinas_darmasaba", + "nama": "Kepala Dusun Banjar Dinas Darmasaba", + "deskripsi": "Pimpinan wilayah Banjar Dinas Darmasaba", + "hierarki": 3, + "parentId": "sekretaris_desa" + }, + { + "id": "staf_desa", + "nama": "Staf Desa", + "deskripsi": "Staf Desa", + "hierarki": 3, + "parentId": "sekretaris_desa" + } + ] + ] + \ No newline at end of file diff --git a/prisma/data/ekonomi/struktur-organisasi/posisi-organisasi.json b/prisma/data/ekonomi/struktur-organisasi/posisi-organisasi.json deleted file mode 100644 index 7596e168..00000000 --- a/prisma/data/ekonomi/struktur-organisasi/posisi-organisasi.json +++ /dev/null @@ -1,27 +0,0 @@ -[ - { - "id": "kepala_desa", - "nama": "Kepala Desa", - "deskripsi": "Kepala Desa", - "hierarki": 1 - }, - { - "id": "sekretaris_desa", - "nama": "Sekretaris Desa", - "deskripsi": "Sekretaris Desa", - "hierarki": 2 - }, - { - "id": "bendahara_desa", - "nama": "Bendahara Desa", - "deskripsi": "Bendahara Desa", - "hierarki": 3 - }, - { - "id": "staff_umum", - "nama": "Staff Umum", - "deskripsi": "Staff Umum", - "hierarki": 4 - } - ] - \ No newline at end of file diff --git a/prisma/data/ppid/struktur-ppid/pegawai-PPID.json b/prisma/data/ppid/struktur-ppid/pegawai-PPID.json index e7a7a9cf..713cb799 100644 --- a/prisma/data/ppid/struktur-ppid/pegawai-PPID.json +++ b/prisma/data/ppid/struktur-ppid/pegawai-PPID.json @@ -1,6 +1,6 @@ [ { - "id": "550e8400-e29b-41d4-a716-446655440001", + "id": "cmgewz4gt000704ib91i3f169", "namaLengkap": "Ida Bagus Surya Prabhawa Manuaba, S.H.,M.H., NL.P.", "gelarAkademik": "S.H.,M.H.,NL.P.", "tanggalMasuk": "2020-01-01T00:00:00.000Z", @@ -11,7 +11,7 @@ "isActive": true }, { - "id": "550e8400-e29b-41d4-a716-446655440002", + "id": "cmgewxfvw000004ibee5013f4", "namaLengkap": "I Ketut Suwanta", "gelarAkademik": "S.Pt", "tanggalMasuk": "2020-02-01T00:00:00.000Z", @@ -22,7 +22,7 @@ "isActive": true }, { - "id": "550e8400-e29b-41d4-a716-446655440006", + "id": "cmgewxvqw000104ibgm5l8fzs", "namaLengkap": "Ni Wayan Supardiati", "gelarAkademik": "S.Pd", "tanggalMasuk": "2020-02-01T00:00:00.000Z", @@ -33,7 +33,7 @@ "isActive": true }, { - "id": "550e8400-e29b-41d4-a716-446655440011", + "id": "cmgewy1g9000204ib2n7hbx0i", "namaLengkap": "I Wayan Agus Juni Artha Saputra", "gelarAkademik": "S.T.", "tanggalMasuk": "2020-02-01T00:00:00.000Z", @@ -44,7 +44,7 @@ "isActive": true }, { - "id": "550e8400-e29b-41d4-a716-446655440012", + "id": "cmgewybah000304ibgqhn1gm2", "namaLengkap": "I Wayan Sueca", "gelarAkademik": "S.H.", "tanggalMasuk": "2020-02-01T00:00:00.000Z", @@ -55,7 +55,7 @@ "isActive": true }, { - "id": "550e8400-e29b-41d4-a716-446655440017", + "id": "cmgewygqz000404ib20sv8nvg", "namaLengkap": "Si Gede Ketut Astawa", "gelarAkademik": "S.T.", "tanggalMasuk": "2020-02-01T00:00:00.000Z", @@ -66,7 +66,7 @@ "isActive": true }, { - "id": "550e8400-e29b-41d4-a716-446655440018", + "id": "cmgewyos1000504ibcu8o2gyk", "namaLengkap": "I Kadek Arya Minarta", "gelarAkademik": "S.T.", "tanggalMasuk": "2020-02-01T00:00:00.000Z", @@ -77,7 +77,7 @@ "isActive": true }, { - "id": "550e8400-e29b-41d4-a716-446655440021", + "id": "cmgewyxk7000604ib8djs3i6c", "namaLengkap": "I Gede Andika Pradnya Diputra", "gelarAkademik": "S.E.", "tanggalMasuk": "2020-02-01T00:00:00.000Z", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5e503cd0..2710da66 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -81,7 +81,7 @@ model FileStorage { PelayananSuratKeteranganImage PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage") PelayananSuratKeteranganImage2 PelayananSuratKeterangan[] @relation("PelayananSuratKeteranganImage2") PasarDesa PasarDesa[] - Pegawai Pegawai[] + PegawaiBumDes PegawaiBumDes[] DesaDigital DesaDigital[] InfoTekno InfoTekno[] PengaduanMasyarakat PengaduanMasyarakat[] @@ -101,6 +101,7 @@ model FileStorage { MitraKolaborasi MitraKolaborasi[] ArtikelKesehatan ArtikelKesehatan[] + StrukturBumDes StrukturBumDes[] } //========================================= MENU LANDING PAGE ========================================= // @@ -286,49 +287,51 @@ model StrukturPPID { } model PosisiOrganisasiPPID { - id String @id @default(cuid()) - nama String @db.VarChar(100) - deskripsi String? @db.Text - hierarki Int - pegawai PegawaiPPID[] - strukturOrganisasi StrukturPPID[] // Relasi balik - parentId String? - isActive Boolean @default(true) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - parent PosisiOrganisasiPPID? @relation("Parent", fields: [parentId], references: [id]) - children PosisiOrganisasiPPID[] @relation("Parent") + id String @id @default(cuid()) + nama String @db.VarChar(100) + deskripsi String? @db.Text + hierarki Int + pegawai PegawaiPPID[] + strukturOrganisasi StrukturPPID[] // Relasi balik + parentId String? + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + parent PosisiOrganisasiPPID? @relation("Parent", fields: [parentId], references: [id]) + children PosisiOrganisasiPPID[] @relation("Parent") + StrukturOrganisasiPPID StrukturOrganisasiPPID[] } model PegawaiPPID { - id String @id @default(cuid()) - namaLengkap String @db.VarChar(255) - gelarAkademik String? @db.VarChar(100) - image FileStorage? @relation(fields: [imageId], references: [id]) - imageId String? - tanggalMasuk DateTime? @db.Date - email String? @unique @db.VarChar(255) - telepon String? @db.VarChar(20) - alamat String? @db.Text - posisiId String @db.VarChar(50) - isActive Boolean @default(true) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - posisi PosisiOrganisasiPPID @relation(fields: [posisiId], references: [id]) - strukturOrganisasi StrukturPPID[] // Relasi balik + id String @id @default(cuid()) + namaLengkap String @db.VarChar(255) + gelarAkademik String? @db.VarChar(100) + image FileStorage? @relation(fields: [imageId], references: [id]) + imageId String? + tanggalMasuk DateTime? @db.Date + email String? @unique @db.VarChar(255) + telepon String? @db.VarChar(20) + alamat String? @db.Text + posisiId String @db.VarChar(50) + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + posisi PosisiOrganisasiPPID @relation(fields: [posisiId], references: [id]) + strukturOrganisasi StrukturPPID[] // Relasi balik + StrukturOrganisasiPPID StrukturOrganisasiPPID[] } model StrukturOrganisasiPPID { - id String @id @default(uuid()) - posisiOrganisasiId String @db.VarChar(50) - pegawaiId String @db.Uuid - hubunganOrganisasiId String @db.Uuid - posisiOrganisasi PosisiOrganisasi @relation(fields: [posisiOrganisasiId], references: [id]) - pegawai Pegawai @relation(fields: [pegawaiId], references: [id]) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(uuid()) + posisiOrganisasiId String @db.VarChar(50) + pegawaiId String + hubunganOrganisasiId String + posisiOrganisasi PosisiOrganisasiPPID @relation(fields: [posisiOrganisasiId], references: [id]) + pegawai PegawaiPPID @relation(fields: [pegawaiId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt deletedAt DateTime? - isActive Boolean @default(true) + isActive Boolean @default(true) } // ========================================= VISI MISI PPID ========================================= // @@ -850,7 +853,7 @@ model JadwalKegiatan { syaratKetentuanJadwalKegiatanId String dokumenjadwalkegiatan DokumenJadwalKegiatan @relation(fields: [dokumenJadwalKegiatanId], references: [id]) dokumenJadwalKegiatanId String - pendaftaranjadwalkegiatan PendaftaranJadwalKegiatan? @relation(fields: [pendaftaranJadwalKegiatanId], references: [id]) + pendaftaranjadwalkegiatan PendaftaranJadwalKegiatan? @relation(fields: [pendaftaranJadwalKegiatanId], references: [id]) pendaftaranJadwalKegiatanId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -1394,79 +1397,67 @@ model LowonganPekerjaan { // ========================================= STRUKTUR ORGANISASI ========================================= // -model PosisiOrganisasi { - id String @id @default(uuid()) @db.VarChar(50) - nama String @db.VarChar(100) - deskripsi String? @db.Text - hierarki Int - - pegawai Pegawai[] - strukturOrganisasi StrukturOrganisasi[] // Relasi balik - StrukturOrganisasiPPID StrukturOrganisasiPPID[] - isActive Boolean @default(true) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - @@map("posisi_organisasi") +model StrukturBumDes { + id String @id @default(cuid()) + name String @db.Text + image FileStorage? @relation(fields: [imageId], references: [id]) + imageId String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) + PosisiOrganisasiBumDes PosisiOrganisasiBumDes? @relation(fields: [posisiOrganisasiBumDesId], references: [id]) + posisiOrganisasiBumDesId String? + PegawaiBumDes PegawaiBumDes? @relation(fields: [pegawaiBumDesId], references: [id]) + pegawaiBumDesId String? } -model Pegawai { - id String @id @default(uuid()) @db.Uuid - namaLengkap String @db.VarChar(255) - gelarAkademik String? @db.VarChar(100) - image FileStorage? @relation(fields: [imageId], references: [id]) - imageId String? - tanggalMasuk DateTime? @db.Date - email String? @unique @db.VarChar(255) - telepon String? @db.VarChar(20) - alamat String? @db.Text - posisiId String @db.VarChar(50) - isActive Boolean @default(true) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - posisi PosisiOrganisasi @relation(fields: [posisiId], references: [id]) - - sebagaiAtasan HubunganOrganisasi[] @relation("AtasanToBawahan") - sebagaiBawahan HubunganOrganisasi[] @relation("BawahanToAtasan") - - strukturOrganisasi StrukturOrganisasi[] // Relasi balik - StrukturOrganisasiPPID StrukturOrganisasiPPID[] - - @@map("pegawai") +model PosisiOrganisasiBumDes { + id String @id @default(cuid()) + nama String @db.VarChar(100) + deskripsi String? @db.Text + hierarki Int + pegawai PegawaiBumDes[] + strukturOrganisasi StrukturBumDes[] // Relasi balik + parentId String? + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + parent PosisiOrganisasiBumDes? @relation("Parent", fields: [parentId], references: [id]) + children PosisiOrganisasiBumDes[] @relation("Parent") + StrukturOrganisasiBumDes StrukturOrganisasiBumDes[] } -model HubunganOrganisasi { - id String @id @default(uuid()) @db.Uuid - atasanId String @db.Uuid - bawahanId String @db.Uuid - tipe String? @db.VarChar(50) - - atasan Pegawai @relation("AtasanToBawahan", fields: [atasanId], references: [id]) - bawahan Pegawai @relation("BawahanToAtasan", fields: [bawahanId], references: [id]) - - strukturOrganisasi StrukturOrganisasi[] // Relasi balik - - @@unique([atasanId, bawahanId]) - @@map("hubungan_organisasi") +model PegawaiBumDes { + id String @id @default(cuid()) + namaLengkap String @db.VarChar(255) + gelarAkademik String? @db.VarChar(100) + image FileStorage? @relation(fields: [imageId], references: [id]) + imageId String? + tanggalMasuk DateTime? @db.Date + email String? @unique @db.VarChar(255) + telepon String? @db.VarChar(20) + alamat String? @db.Text + posisiId String @db.VarChar(50) + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + posisi PosisiOrganisasiBumDes @relation(fields: [posisiId], references: [id]) + strukturOrganisasi StrukturBumDes[] // Relasi balik + StrukturOrganisasiBumDes StrukturOrganisasiBumDes[] } -model StrukturOrganisasi { - id String @id @default(uuid()) - posisiOrganisasiId String @db.VarChar(50) - pegawaiId String @db.Uuid - hubunganOrganisasiId String @db.Uuid - - posisiOrganisasi PosisiOrganisasi @relation(fields: [posisiOrganisasiId], references: [id]) - pegawai Pegawai @relation(fields: [pegawaiId], references: [id]) - hubunganOrganisasi HubunganOrganisasi @relation(fields: [hubunganOrganisasiId], references: [id]) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - deletedAt DateTime? - isActive Boolean @default(true) - - @@map("struktur_organisasi") +model StrukturOrganisasiBumDes { + id String @id @default(uuid()) + posisiOrganisasiId String @db.VarChar(50) + pegawaiId String + hubunganOrganisasiId String + posisiOrganisasi PosisiOrganisasiBumDes @relation(fields: [posisiOrganisasiId], references: [id]) + pegawai PegawaiBumDes @relation(fields: [pegawaiId], references: [id]) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime? + isActive Boolean @default(true) } // ========================================= PROGRAM KEMISKINAN ========================================= // diff --git a/prisma/seed.ts b/prisma/seed.ts index c3c7af27..beeb99f8 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -31,9 +31,8 @@ import sejarahDesa from "./data/desa/profile/sejarah_desa.json"; import visiMisiDesa from "./data/desa/profile/visi_misi_desa.json"; import detailDataPengangguran from "./data/ekonomi/jumlah-pengangguran/detail-data-pengangguran.json"; import kategoriProduk from "./data/ekonomi/pasar-desa/kategori-produk.json"; -import hubunganOrganisasi from "./data/ekonomi/struktur-organisasi/hubungan-organisasi.json"; -import pegawai from "./data/ekonomi/struktur-organisasi/pegawai.json"; -import posisiOrganisasi from "./data/ekonomi/struktur-organisasi/posisi-organisasi.json"; +import pegawai from "./data/ekonomi/struktur-organisasi/pegawai-bumdes.json"; +import posisiOrganisasi from "./data/ekonomi/struktur-organisasi/posisi-organisasi-bumdes.json"; import kategoriBerita from "./data/kategori-berita.json"; import contohEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/contoh-kegiatan-di-desa-darmasaba.json"; import materiEdukasiLingkungan from "./data/lingkungan/edukasi-lingkungan/materi-edukasi-yang-diberikan.json"; @@ -807,28 +806,34 @@ import { safeSeedUnique } from "./safeseedUnique"; } console.log("kategori produk success ..."); - for (const p of posisiOrganisasi) { - await prisma.posisiOrganisasi.upsert({ - where: { - id: p.id, - }, - update: { - nama: p.nama, - deskripsi: p.deskripsi, - hierarki: p.hierarki, - }, - create: { - id: p.id, - nama: p.nama, - deskripsi: p.deskripsi, - hierarki: p.hierarki, - }, + const flattenedPosisiBumdes = posisiOrganisasi.flat(); + + // ✅ Urutkan berdasarkan hierarki + const sortedPosisiBumdes = flattenedPosisiBumdes.sort((a, b) => a.hierarki - b.hierarki); + + for (const p of sortedPosisiBumdes) { + console.log(`Seeding: ${p.nama} (id: ${p.id}, parent: ${p.parentId})`); + + if (p.parentId) { + const parentExists = flattenedPosisi.some((pos) => pos.id === p.parentId); + if (!parentExists) { + console.warn( + `⚠️ Parent tidak ditemukan: ${p.parentId} untuk ${p.nama}` + ); + continue; + } + } + + await prisma.posisiOrganisasiBumDes.upsert({ + where: { id: p.id }, + update: p, + create: p, }); } - console.log("posisi organisasi success ..."); + console.log("posisi organisasi berhasil"); for (const p of pegawai) { - await prisma.pegawai.upsert({ + await prisma.pegawaiBumDes.upsert({ where: { id: p.id, }, @@ -857,26 +862,6 @@ import { safeSeedUnique } from "./safeseedUnique"; } console.log("pegawai success ..."); - for (const p of hubunganOrganisasi) { - await prisma.hubunganOrganisasi.upsert({ - where: { - atasanId_bawahanId: { - atasanId: p.atasanId, - bawahanId: p.bawahanId, - }, - }, - update: { - tipe: p.tipe, - }, - create: { - atasanId: p.atasanId, - bawahanId: p.bawahanId, - tipe: p.tipe, - }, - }); - } - console.log("hubungan organisasi success ..."); - for (const d of detailDataPengangguran) { await prisma.detailDataPengangguran.upsert({ where: { diff --git a/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts b/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts index 9fc870d2..cef5c3fa 100644 --- a/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts +++ b/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts @@ -1,9 +1,173 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { proxy } from "valtio"; -import { z } from "zod"; -import { toast } from "react-toastify"; import ApiFetch from "@/lib/api-fetch"; import { Prisma } from "@prisma/client"; +import { toast } from "react-toastify"; +import { proxy } from "valtio"; +import { z } from "zod"; + +const templateForm = z.object({ + name: z.string().min(3, "Nama minimal 3 karakter"), + imageId: z.string().min(1, "Gambar wajib dipilih"), +}); + +const defaultForm = { + name: "", + imageId: "", +}; + +type StrukturBumDesForm = Prisma.StrukturBumDesGetPayload<{ + select: { + id: true; + name: true; + imageId: true; + image?: { + select: { + link: true; + }; + }; + }; +}>; + +const stateStruktur = proxy({ + struktur: { + data: null as StrukturBumDesForm | null, + loading: false, + error: null as string | null, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + this.loading = true; + this.error = null; + + try { + const response = await fetch(`/api/ekonomi/struktur-organisasi/${id}`); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const result = await response.json(); + + if (result.success) { + this.data = result.data; + return result.data; + } else { + throw new Error(result.message || "Gagal mengambil data struktur"); + } + } catch (error) { + const errorMessage = (error as Error).message; + this.error = errorMessage; + console.error("Load struktur error:", errorMessage); + toast.error("Terjadi kesalahan saat mengambil data struktur"); + return null; + } finally { + this.loading = false; + } + }, + + reset() { + this.data = null; + this.error = null; + this.loading = false; + }, + }, + + editStruktur: { + id: "", + form: { ...defaultForm }, + loading: false, + error: null as string | null, + isReadOnly: false, + + initialize(strukturData: StrukturBumDesForm) { + this.id = strukturData.id; + this.isReadOnly = false; + this.form = { + name: strukturData.name || "", + imageId: strukturData.imageId || "", + }; + }, + + updateField(field: keyof typeof defaultForm, value: string) { + this.form[field] = value; + }, + + async submit() { + const validation = templateForm.safeParse(this.form); + + if (!validation.success) { + const errors = validation.error.issues + .map((issue) => `${issue.path.join(".")}: ${issue.message}`) + .join(", "); + toast.error(`Form tidak valid: ${errors}`); + return false; + } + + this.loading = true; + this.error = null; + + try { + const response = await fetch(`/api/ekonomi/struktur-organisasi/${this.id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(this.form), + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error( + errorData.message || `HTTP error! status: ${response.status}` + ); + } + + const result = await response.json(); + + if (result.success) { + toast.success("Berhasil update struktur"); + await stateStruktur.struktur.load(this.id); + return true; + } else { + throw new Error(result.message || "Gagal update struktur"); + } + } catch (error) { + const errorMessage = (error as Error).message; + this.error = errorMessage; + console.error("Update struktur error:", errorMessage); + toast.error("Terjadi kesalahan saat update struktur"); + return false; + } finally { + this.loading = false; + } + }, + + reset() { + this.id = ""; + this.form = { ...defaultForm }; + this.error = null; + this.loading = false; + this.isReadOnly = false; + }, + }, + + async loadForEdit(id: string) { + const strukturData = await this.struktur.load(id); + if (strukturData) { + this.editStruktur.initialize(strukturData); + } + return strukturData; + }, + + reset() { + this.struktur.reset(); + this.editStruktur.reset(); + }, +}); const templatePosisiOrganisasi = z.object({ nama: z.string().min(1, "Nama harus diisi"), @@ -30,9 +194,7 @@ const posisiOrganisasi = proxy({ try { this.loading = true; - const res = await ApiFetch.api.ekonomi["struktur-organisasi"][ - "posisi-organisasi" - ]["create"].post(this.form); + const res = await ApiFetch.api.ekonomi['struktur-organisasi']['posisi-organisasi']['create'].post(this.form); if (res.status === 200) { toast.success("Berhasil menambahkan posisi organisasi"); posisiOrganisasi.findMany.load(); @@ -52,6 +214,29 @@ const posisiOrganisasi = proxy({ }, }, + findUnique: { + data: null as Prisma.StrukturOrganisasiBumDesGetPayload<{ + omit: { isActive: true }; + }> | null, + async load(id: string) { + try { + const res = await fetch( + `/api/ekonomi/struktur-organisasi/posisi-organisasi/${id}` + ); + if (res.ok) { + const data = await res.json(); + posisiOrganisasi.findUnique.data = data.data ?? null; + } else { + console.error("Failed to fetch posisiOrganisasi:", res.statusText); + posisiOrganisasi.findUnique.data = null; + } + } catch (error) { + console.error("Error fetching posisiOrganisasi:", error); + posisiOrganisasi.findUnique.data = null; + } + }, + }, + edit: { id: "", form: { ...posisiOrganisasiDefaultForm }, @@ -165,17 +350,17 @@ const posisiOrganisasi = proxy({ totalPages: 1, loading: false, search: "", - load: async (page = 1, limit = 10, search = "") => { - posisiOrganisasi.findMany.loading = true; // ✅ Akses langsung via nama path + load: async (page = 1, limit?: number, search = "") => { + const appliedLimit = limit ?? 10; posisiOrganisasi.findMany.page = page; posisiOrganisasi.findMany.search = search; - + try { - const query: any = { page, limit }; + const query: any = { page, limit: appliedLimit }; if (search) query.search = search; - - const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["posisi-organisasi"]["find-many"].get({ query }); - + + const res = await ApiFetch.api.ekonomi['struktur-organisasi']['posisi-organisasi']['find-many'].get({ query }); + if (res.status === 200 && res.data?.success) { posisiOrganisasi.findMany.data = res.data.data ?? []; posisiOrganisasi.findMany.totalPages = res.data.totalPages ?? 1; @@ -192,7 +377,42 @@ const posisiOrganisasi = proxy({ } }, }, + findManyAll: { + data: [] as Array<{ + id: string; + nama: string; + deskripsi: string | null; + hierarki: number; + }>, + loading: false, + search: "", + load: async (search = "") => { + // Change to arrow function + posisiOrganisasi.findManyAll.loading = true; // Use the full path to access the property + posisiOrganisasi.findManyAll.search = search; + try { + const query: any = { search }; + if (search) query.search = search; + const res = await ApiFetch.api.ekonomi['struktur-organisasi']['posisi-organisasi']['find-many-all'].get({ + query, + }); + + if (res.status === 200 && res.data?.success) { + posisiOrganisasi.findManyAll.data = res.data.data || []; + + } else { + console.error("Failed to load posisiOrganisasi:", res.data?.message); + posisiOrganisasi.findManyAll.data = []; + } + } catch (error) { + console.error("Error loading posisiOrganisasi:", error); + posisiOrganisasi.findManyAll.data = []; + } finally { + posisiOrganisasi.findManyAll.loading = false; + } + }, + }, delete: { loading: false, async byId(id: string) { @@ -231,12 +451,12 @@ const posisiOrganisasi = proxy({ const templatePegawai = z.object({ namaLengkap: z.string().min(1, "Nama wajib diisi"), - gelarAkademik: z.string().optional(), - imageId: z.string().nullable().optional(), - tanggalMasuk: z.string().optional(), // ISO format + gelarAkademik: z.string().min(1, "Gelar Akademik wajib diisi"), + imageId: z.string().min(1, "Gambar wajib dipilih"), + tanggalMasuk: z.string().min(1, "Tanggal masuk wajib diisi"), // ISO format email: z.string().email("Email tidak valid").optional(), - telepon: z.string().optional(), - alamat: z.string().optional(), + telepon: z.string().min(1, "Telepom wajib diisi"), + alamat: z.string().min(1, "Alamat wajib diisi"), posisiId: z.string().min(1, "Posisi wajib diisi"), isActive: z.boolean().default(true), }); @@ -267,9 +487,9 @@ const pegawai = proxy({ try { pegawai.create.loading = true; - const res = await ApiFetch.api.ekonomi["struktur-organisasi"][ - "pegawai" - ]["create"].post(pegawai.create.form); + const res = await ApiFetch.api.ekonomi['struktur-organisasi'].pegawai['create'].post( + pegawai.create.form + ); if (res.status === 200) { toast.success("Pegawai berhasil ditambahkan"); await pegawai.findMany.load(); @@ -286,45 +506,56 @@ const pegawai = proxy({ }, // In struktur-organisasi.ts -findMany: { - data: null as any[] | null, - page: 1, - totalPages: 1, - total: 0, - loading: false, - load: async (page = 1, limit = 10) => { // Change to arrow function - pegawai.findMany.loading = true; // Use the full path to access the property - pegawai.findMany.page = page; - try { - const res = await ApiFetch.api.ekonomi["struktur-organisasi"][ - "pegawai" - ]["find-many"].get({ - query: { page, limit }, - }); + findMany: { + data: null as + | Prisma.PegawaiBumDesGetPayload<{ + include: { + image: true; + posisi: true; + }; + }>[] + | null, + page: 1, + totalPages: 1, + total: 0, + loading: false, + search: "", + load: async (page = 1, limit = 10, search = "") => { + // Change to arrow function + pegawai.findMany.loading = true; // Use the full path to access the property + pegawai.findMany.page = page; + pegawai.findMany.search = search; + try { + const query: any = { page, limit }; + if (search) query.search = search; - if (res.status === 200 && res.data?.success) { - pegawai.findMany.data = res.data.data || []; - pegawai.findMany.total = res.data.total || 0; - pegawai.findMany.totalPages = res.data.totalPages || 1; - } else { - console.error("Failed to load pegawai:", res.data?.message); + const res = await ApiFetch.api.ekonomi['struktur-organisasi'].pegawai['find-many'].get({ + query, + }); + + if (res.status === 200 && res.data?.success) { + pegawai.findMany.data = res.data.data || []; + pegawai.findMany.total = res.data.total || 0; + pegawai.findMany.totalPages = res.data.totalPages || 1; + } else { + console.error("Failed to load pegawai:", res.data?.message); + pegawai.findMany.data = []; + pegawai.findMany.total = 0; + pegawai.findMany.totalPages = 1; + } + } catch (error) { + console.error("Error loading pegawai:", error); pegawai.findMany.data = []; pegawai.findMany.total = 0; pegawai.findMany.totalPages = 1; + } finally { + pegawai.findMany.loading = false; } - } catch (error) { - console.error("Error loading pegawai:", error); - pegawai.findMany.data = []; - pegawai.findMany.total = 0; - pegawai.findMany.totalPages = 1; - } finally { - pegawai.findMany.loading = false; - } + }, }, -}, findUnique: { data: null as - | (Prisma.PegawaiGetPayload<{ + | (Prisma.PegawaiBumDesGetPayload<{ include: { posisi: true; image: true }; }> & { isActive: boolean }) | null, @@ -350,12 +581,9 @@ findMany: { if (!id) return toast.warn("ID tidak valid"); try { pegawai.delete.loading = true; - const res = await fetch( - `/api/ekonomi/struktur-organisasi/pegawai/del/${id}`, - { - method: "DELETE", - } - ); + const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/del/${id}`, { + method: "DELETE", + }); const json = await res.json(); if (res.ok) { toast.success(json.message ?? "Berhasil hapus pegawai"); @@ -372,6 +600,31 @@ findMany: { }, }, + nonActive: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + try { + pegawai.nonActive.loading = true; + const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/non-active/${id}`, { + method: "DELETE", // biasanya nonActive pakai PATCH + }); + const json = await res.json(); + if (res.ok) { + toast.success(json.message ?? "Pegawai berhasil dinonaktifkan"); + await pegawai.findMany.load(); // refresh data + } else { + toast.error(json.message ?? "Gagal menonaktifkan pegawai"); + } + } catch (error) { + console.error("Gagal nonActive:", error); + toast.error("Terjadi kesalahan saat menonaktifkan pegawai"); + } finally { + pegawai.nonActive.loading = false; + } + }, + }, + edit: { id: "", form: { ...pegawaiDefaultForm }, @@ -384,15 +637,12 @@ findMany: { } try { - const response = await fetch( - `/api/ekonomi/struktur-organisasi/pegawai/${id}`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - } - ); + const response = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${id}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); @@ -503,299 +753,10 @@ findMany: { }, }); -// Schema Zod untuk form validasi -const templateHubunganOrganisasiForm = z.object({ - atasanId: z.string().min(1, "Atasan wajib dipilih"), - bawahanId: z.string().min(1, "Bawahan wajib dipilih"), - tipe: z.string().optional(), -}); - -// Default form state -const defaultHubunganOrganisasiForm = { - atasanId: "", - bawahanId: "", - tipe: "", -}; - -// ====================== STATE =========================== -const hubunganOrganisasi = proxy({ - create: { - form: { ...defaultHubunganOrganisasiForm }, - loading: false, - async create() { - const cek = templateHubunganOrganisasiForm.safeParse( - hubunganOrganisasi.create.form - ); - if (!cek.success) { - const err = `[${cek.error.issues - .map((v) => `${v.path.join(".")}: ${v.message}`) - .join("\n")}]`; - return toast.error(err); - } - - try { - hubunganOrganisasi.create.loading = true; - const res = await ApiFetch.api.ekonomi["struktur-organisasi"][ - "hubungan-organisasi" - ]["create"].post(hubunganOrganisasi.create.form); - - if (res.status === 200 && res.data?.success) { - hubunganOrganisasi.findMany.load(); - return toast.success("Berhasil menambahkan hubungan organisasi"); - } else { - return toast.error(res.data?.message || "Gagal menambahkan data"); - } - } catch (error) { - console.error("Gagal create:", error); - toast.error("Terjadi kesalahan saat menambahkan"); - } finally { - hubunganOrganisasi.create.loading = false; - } - }, - }, - findMany: { - data: null as Array<{ - id: string; - atasanId: string; - bawahanId: string; - tipe?: string | null; - atasan: { - id: string; - namaLengkap: string; - gelarAkademik: string | null; - imageId: string | null; - tanggalMasuk: Date | null; - email: string | null; - telepon: string | null; - alamat: string | null; - posisiId: string; - isActive: boolean; - createdAt: Date; - updatedAt: Date; - }; - bawahan: { - id: string; - namaLengkap: string; - gelarAkademik: string | null; - imageId: string | null; - tanggalMasuk: Date | null; - email: string | null; - telepon: string | null; - alamat: string | null; - posisiId: string; - isActive: boolean; - createdAt: Date; - updatedAt: Date; - }; - }> | null, - - async load() { - try { - const res = await ApiFetch.api.ekonomi["struktur-organisasi"][ - "hubungan-organisasi" - ]["find-many"].get(); - - if (res.status === 200) { - hubunganOrganisasi.findMany.data = (res.data?.data ?? []).map( - (item: any) => ({ - ...item, - atasan: item.atasan - ? { - ...item.atasan, - isActive: item.atasan.isActive ?? item.atasan.aktif ?? true, - } - : null, - bawahan: item.bawahan - ? { - ...item.bawahan, - isActive: - item.bawahan.isActive ?? item.bawahan.aktif ?? true, - } - : null, - }) - ); - } else { - hubunganOrganisasi.findMany.data = []; - } - } catch (error) { - console.error("Fetch list error:", error); - toast.error("Gagal memuat data hubungan organisasi"); - hubunganOrganisasi.findMany.data = []; - } - }, - }, - - findUnique: { - data: null as { - id: string; - atasanId: string; - bawahanId: string; - tipe?: string | null; - atasan?: { - id: string; - namaLengkap: string; - gelarAkademik: string | null; - imageId: string; - tanggalMasuk: Date | null; - email: string | null; - telepon: string | null; - alamat: string | null; - posisiId: string; - aktif: boolean; - createdAt: Date; - updatedAt: Date; - }; - bawahan?: { - id: string; - namaLengkap: string; - gelarAkademik: string | null; - imageId: string; - tanggalMasuk: Date | null; - email: string | null; - telepon: string | null; - alamat: string | null; - posisiId: string; - aktif: boolean; - createdAt: Date; - updatedAt: Date; - }; - } | null, - - async load(id: string) { - try { - const res = await fetch( - `/api/ekonomi/struktur-organisasi/hubungan-organisasi/${id}` - ); - const result = await res.json(); - - if (res.ok && result?.success) { - hubunganOrganisasi.findUnique.data = result.data; - } else { - hubunganOrganisasi.findUnique.data = null; - toast.error(result?.message || "Gagal mengambil data"); - } - } catch (error) { - console.error("Find unique error:", error); - hubunganOrganisasi.findUnique.data = null; - } - }, - }, - - edit: { - id: "", - form: { ...defaultHubunganOrganisasiForm }, - loading: false, - - async load(id: string) { - if (!id) return toast.warn("ID tidak valid"); - - try { - const res = await fetch( - `/api/ekonomi/struktur-organisasi/hubungan-organisasi/${id}` - ); - const result = await res.json(); - - if (res.ok && result?.success) { - const data = result.data; - this.id = data.id; - this.form = { - atasanId: data.atasanId, - bawahanId: data.bawahanId, - tipe: data.tipe || "", - }; - return data; - } else { - throw new Error(result?.message || "Gagal memuat data"); - } - } catch (error) { - console.error("Error loading:", error); - toast.error( - error instanceof Error ? error.message : "Gagal memuat data" - ); - return null; - } - }, - - async update() { - const cek = templateHubunganOrganisasiForm.safeParse(this.form); - if (!cek.success) { - const err = `[${cek.error.issues - .map((v) => `${v.path.join(".")}: ${v.message}`) - .join("\n")}]`; - return toast.error(err); - } - - try { - this.loading = true; - const res = await fetch( - `/api/ekonomi/struktur-organisasi/hubungan-organisasi/${this.id}`, - { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(this.form), - } - ); - - const result = await res.json(); - if (res.ok && result.success) { - await hubunganOrganisasi.findMany.load(); - toast.success("Berhasil mengupdate hubungan organisasi"); - return true; - } else { - throw new Error(result?.message || "Gagal mengupdate"); - } - } catch (error) { - console.error("Update error:", error); - toast.error(error instanceof Error ? error.message : "Gagal update"); - return false; - } finally { - this.loading = false; - } - }, - - reset() { - hubunganOrganisasi.edit.id = ""; - hubunganOrganisasi.edit.form = { ...defaultHubunganOrganisasiForm }; - }, - }, - - delete: { - loading: false, - async byId(id: string) { - if (!id) return toast.warn("ID tidak valid"); - - try { - hubunganOrganisasi.delete.loading = true; - const res = await fetch( - `/api/ekonomi/struktur-organisasi/hubungan-organisasi/del/${id}`, - { - method: "DELETE", - } - ); - - const result = await res.json(); - if (res.ok && result?.success) { - toast.success("Hubungan organisasi berhasil dihapus"); - hubunganOrganisasi.findMany.load(); - } else { - toast.error(result?.message || "Gagal menghapus hubungan organisasi"); - } - } catch (error) { - console.error("Delete error:", error); - toast.error("Terjadi kesalahan saat menghapus"); - } finally { - hubunganOrganisasi.delete.loading = false; - } - }, - }, -}); - -const strukturorganisasiState = proxy({ +const stateStrukturBumDes = proxy({ + stateStruktur, posisiOrganisasi, pegawai, - hubunganOrganisasi, }); -export default strukturorganisasiState; +export default stateStrukturBumDes; diff --git a/src/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID.ts b/src/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID.ts index bcc7ed49..7c390f1b 100644 --- a/src/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID.ts +++ b/src/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID.ts @@ -563,7 +563,7 @@ const pegawai = proxy({ }, findUnique: { data: null as - | (Prisma.PegawaiGetPayload<{ + | (Prisma.PegawaiPPIDGetPayload<{ include: { posisi: true; image: true }; }> & { isActive: boolean }) | null, diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/_lib/layoutTabs.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/_lib/layoutTabs.tsx index e920da92..4425aa88 100644 --- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/_lib/layoutTabs.tsx +++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/_lib/layoutTabs.tsx @@ -2,6 +2,7 @@ 'use client' import colors from '@/con/colors'; import { + ScrollArea, Stack, Tabs, TabsList, @@ -9,28 +10,20 @@ import { TabsTab, Title, Tooltip, - ScrollArea, } from '@mantine/core'; +import { + IconBuildingCommunity, + IconHierarchy, + IconUsers +} from '@tabler/icons-react'; import { usePathname, useRouter } from 'next/navigation'; import React, { useEffect, useState } from 'react'; -import { - IconHierarchy, - IconUsers, - IconGitBranch, -} from '@tabler/icons-react'; function LayoutTabs({ children }: { children: React.ReactNode }) { const router = useRouter(); const pathname = usePathname(); const tabs = [ - { - label: "Posisi Organisasi", - value: "posisiorganisasi", - href: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi", - icon: , - tooltip: "Kelola daftar posisi organisasi", - }, { label: "Pegawai", value: "pegawai", @@ -39,12 +32,19 @@ function LayoutTabs({ children }: { children: React.ReactNode }) { tooltip: "Kelola data pegawai BUMDesa", }, { - label: "Hubungan Organisasi", - value: "hubunganorganisasi", - href: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi", - icon: , - tooltip: "Atur hubungan antar posisi organisasi", + label: "Posisi Organisasi", + value: "posisiorganisasi", + href: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi", + icon: , + tooltip: "Kelola daftar posisi organisasi", }, + { + label: "Struktur Organisasi", + value: "strukturorganisasi", + href: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/struktur-organisasi", + icon: , + tooltip: "Kelola struktur organisasi BUMDesa" + } ]; const currentTab = tabs.find((tab) => tab.href === pathname); diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/[id]/page.tsx deleted file mode 100644 index 740bd159..00000000 --- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/[id]/page.tsx +++ /dev/null @@ -1,116 +0,0 @@ -'use client'; - -import { useEffect, useState } from 'react'; -import { useParams, useRouter } from 'next/navigation'; -import { Box, Button, Paper, Select, Stack, TextInput, Title } from '@mantine/core'; -import { toast } from 'react-toastify'; -import { useProxy } from 'valtio/utils'; -import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; - -export default function EditHubunganOrganisasi() { - const router = useRouter(); - const { id } = useParams<{ id: string }>(); - const state = useProxy(strukturorganisasiState.hubunganOrganisasi); - const pegawaiList = strukturorganisasiState.pegawai.findMany.data; - - const [form, setForm] = useState({ - atasanId: '', - bawahanId: '', - tipe: '', - }); - - // load data awal sekali aja - useEffect(() => { - strukturorganisasiState.pegawai.findMany.load(); - - if (id) { - (async () => { - const data = await state.edit.load(id); - if (data) { - setForm({ - atasanId: data.atasanId ?? '', - bawahanId: data.bawahanId ?? '', - tipe: data.tipe ?? '', - }); - } - })(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [id]); - - const handleChange = (field: keyof typeof form, value: string) => { - setForm(prev => ({ - ...prev, - [field]: value, - })); - }; - - const handleSubmit = async () => { - if (!form.atasanId || !form.bawahanId) { - toast.warn('Atasan dan bawahan harus diisi'); - return; - } - - // update global state cuma pas submit - state.edit.id = id; - state.edit.form = form; - - const result = await state.edit.update(); - - if (result) { - toast.success('Data berhasil diperbarui'); - router.push( - '/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi' - ); - } - }; - - return ( - - - - Edit Hubungan Organisasi - - ({ - value: p.id, - label: p.namaLengkap, - })) || [] - } - value={form.atasanId} - onChange={val => handleChange('atasanId', val || '')} - /> - - ({ - value: p.id, - label: p.namaLengkap, - })) || [] - } - value={form.bawahanId} - onChange={val => handleChange('bawahanId', val || '')} - /> - - handleChange('tipe', e.currentTarget.value)} - /> - - - Simpan - - - - - ); -} diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/create/page.tsx deleted file mode 100644 index 32b57b80..00000000 --- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/create/page.tsx +++ /dev/null @@ -1,78 +0,0 @@ -'use client'; - -import { useEffect, useState } from 'react'; -import { useRouter } from 'next/navigation'; -import { Box, Button, Paper, Select, Stack, TextInput, Title } from '@mantine/core'; -import { toast } from 'react-toastify'; -import { useProxy } from 'valtio/utils'; -import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; - -export default function CreateHubunganOrganisasi() { - const router = useRouter(); - const state = useProxy(strukturorganisasiState.hubunganOrganisasi); - const pegawaiList = strukturorganisasiState.pegawai.findMany.data; - - const [form, setForm] = useState({ - atasanId: '', - bawahanId: '', - tipe: '', - }); - - useEffect(() => { - strukturorganisasiState.pegawai.findMany.load(); - }, []); - - const handleSubmit = async () => { - if (!form.atasanId || !form.bawahanId) { - toast.warn("Atasan dan bawahan harus diisi"); - return; - } - - state.create.form = form; - const result = await state.create.create(); - - if (result) { - toast.success("Hubungan Organisasi berhasil ditambahkan"); - router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi'); - } - }; - - return ( - - - - Create Hubungan Organisasi - ({ - value: p.id, - label: p.namaLengkap, - })) || []} - value={form.atasanId} - onChange={(val) => setForm({ ...form, atasanId: val || '' })} - /> - ({ - value: p.id, - label: p.namaLengkap, - })) || []} - value={form.bawahanId} - onChange={(val) => setForm({ ...form, bawahanId: val || '' })} - /> - setForm({ ...form, tipe: e.currentTarget.value })} - /> - Simpan - - - - ); -} diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/page.tsx deleted file mode 100644 index a4018cba..00000000 --- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/page.tsx +++ /dev/null @@ -1,130 +0,0 @@ -'use client' -import colors from '@/con/colors'; -import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; -import { useShallowEffect } from '@mantine/hooks'; -import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import { useProxy } from 'valtio/utils'; -import HeaderSearch from '../../../_com/header'; -import JudulList from '../../../_com/judulList'; -import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi'; -import { useState } from 'react'; -import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; - -function HubunganOrganisasi() { - const [search, setSearch] = useState(""); - - return ( - - } - value={search} - onChange={(e) => setSearch(e.currentTarget.value)} - /> - - - ); -} - - -function ListHubunganOrganisasi({ search }: { search: string }) { - const stateOrganisasi = useProxy(strukturorganisasiState.hubunganOrganisasi); - const [modalHapus, setModalHapus] = useState(false); - const [selectedId, setSelectedId] = useState(null); - const router = useRouter(); - - useShallowEffect(() => { - stateOrganisasi.findMany.load(); - }, []); - - const handleHapus = () => { - if (selectedId) { - stateOrganisasi.delete.byId(selectedId); - setModalHapus(false); - setSelectedId(null); - } - }; - - const filteredData = (stateOrganisasi.findMany.data || []).filter(item => { - const keyword = search.toLowerCase(); - return ( - item.atasan?.namaLengkap?.toLowerCase().includes(keyword) || - item.bawahan?.namaLengkap?.toLowerCase().includes(keyword) || - item.tipe?.toLowerCase().includes(keyword) - ); - }); - - if (!stateOrganisasi.findMany.data) { - return ( - - - - ); - } - - return ( - - - - - - - Atasan - Bawahan - Tipe - Edit - Hapus - - - - {filteredData - .sort((a, b) => - a.atasan?.namaLengkap.localeCompare(b.atasan?.namaLengkap) - ) - .map((item) => ( - - {item.atasan?.namaLengkap} - {item.bawahan?.namaLengkap} - {item.tipe} - - router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/${item.id}`)} - > - - - - - { - setSelectedId(item.id); - setModalHapus(true); - }} - > - - - - - ))} - - - - - setModalHapus(false)} - onConfirm={handleHapus} - text='Apakah anda yakin ingin menghapus hubungan organisasi ini?' - /> - - ); -} - - -export default HubunganOrganisasi; diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/edit/page.tsx index 35a3a8b9..c1a8eef5 100644 --- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/edit/page.tsx @@ -1,8 +1,9 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable react-hooks/exhaustive-deps */ 'use client' -import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; + +import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; import colors from '@/con/colors'; +import ApiFetch from '@/lib/api-fetch'; import { Box, Button, @@ -13,7 +14,8 @@ import { Stack, Text, TextInput, - Title + Title, + Tooltip } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; @@ -22,30 +24,12 @@ import { useEffect, useState } from 'react'; import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; -interface PreviewImage { - file?: File; - preview: string; -} - -interface PegawaiFormData { - namaLengkap: string; - gelarAkademik: string; - imageId: string | null; - tanggalMasuk: string; - email: string; - telepon: string; - alamat: string; - posisiId: string; - isActive: boolean; -} - -export default function EditPegawai() { +export default function EditPegawaiBumDes() { const router = useRouter(); const { id } = useParams<{ id: string }>(); - const stateOrganisasi = useProxy(strukturorganisasiState.pegawai); + const stateOrganisasi = useProxy(stateStrukturBumDes.pegawai); - const [previewImage, setPreviewImage] = useState(null); - const [formData, setFormData] = useState({ + const [formData, setFormData] = useState({ namaLengkap: '', gelarAkademik: '', imageId: '', @@ -56,16 +40,10 @@ export default function EditPegawai() { posisiId: '', isActive: true, }); + const [previewImage, setPreviewImage] = useState(null); + const [file, setFile] = useState(null); - // helper buat update state formData - const updateForm = (field: keyof PegawaiFormData, value: any) => { - setFormData((prev) => ({ - ...prev, - [field]: value, - })); - }; - - // Format date to YYYY-MM-DD for date input + // Format date for const formatDateForInput = (dateString: string) => { if (!dateString) return ''; const date = new Date(dateString); @@ -73,10 +51,10 @@ export default function EditPegawai() { }; useEffect(() => { - strukturorganisasiState.posisiOrganisasi.findMany.load(); - const loadPegawai = async () => { try { + await stateStrukturBumDes.posisiOrganisasi.findManyAll.load(); + const data = await stateOrganisasi.edit.load(id); if (data) { setFormData({ @@ -95,9 +73,7 @@ export default function EditPegawai() { } } catch (error) { console.error('Error loading pegawai:', error); - toast.error( - error instanceof Error ? error.message : 'Gagal mengambil data pegawai' - ); + toast.error(error instanceof Error ? error.message : 'Gagal mengambil data pegawai'); } }; @@ -107,204 +83,182 @@ export default function EditPegawai() { const handleSubmit = async () => { try { if (!formData.namaLengkap.trim()) { - toast.error('Nama lengkap tidak boleh kosong'); - return; + return toast.error('Nama lengkap tidak boleh kosong'); } - stateOrganisasi.edit.form = { - ...formData, - namaLengkap: formData.namaLengkap.trim(), - gelarAkademik: formData.gelarAkademik.trim(), - imageId: formData.imageId ? formData.imageId.trim() : '', - tanggalMasuk: formData.tanggalMasuk.trim(), - email: formData.email.trim(), - telepon: formData.telepon.trim(), - alamat: formData.alamat.trim(), - posisiId: formData.posisiId.trim(), - }; + // Update global state only on submit + const updatedForm = { ...formData }; - if (id && !stateOrganisasi.edit.id) { - stateOrganisasi.edit.id = id; + if (file) { + const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name }); + const uploaded = res.data?.data; + if (!uploaded?.id) return toast.error('Gagal upload gambar'); + updatedForm.imageId = uploaded.id; } + stateOrganisasi.edit.form = updatedForm; + if (id && !stateOrganisasi.edit.id) stateOrganisasi.edit.id = id; + const success = await stateOrganisasi.edit.submit(); - - if (success) { - router.push( - '/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai' - ); - } + if (success) router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai'); } catch (error) { console.error('Error updating pegawai:', error); - toast.error( - error instanceof Error ? error.message : 'Gagal memperbarui data pegawai' - ); + toast.error(error instanceof Error ? error.message : 'Gagal memperbarui data pegawai'); } }; return ( - - - router.back()} variant="subtle" color={'blue'}> - - - - - - - Edit Data Pegawai + + + + router.back()} p="xs" radius="md"> + + + + Edit Data Pegawai PPID + + + + {/* Nama Lengkap */} updateForm('namaLengkap', e.target.value)} + onChange={(e) => setFormData({ ...formData, namaLengkap: e.target.value })} + required /> + {/* Gelar Akademik */} updateForm('gelarAkademik', e.target.value)} + onChange={(e) => setFormData({ ...formData, gelarAkademik: e.target.value })} /> + {/* Foto Profil */} - - Gambar - - - { - const file = files[0]; - if (file) { - setPreviewImage({ - file, - preview: URL.createObjectURL(file), - }); - } - }} - maxSize={5 * 1024 ** 2} - accept={{ - 'image/*': ['.jpeg', '.jpg', '.png', '.webp'], - }} - > - - - - - - - - - - + Foto Profil + { + const selectedFile = files[0]; + if (selectedFile) { + setFile(selectedFile); + setPreviewImage(URL.createObjectURL(selectedFile)); + } + }} + onReject={() => toast.error('File tidak valid, gunakan format gambar')} + maxSize={5 * 1024 ** 2} + accept={{ 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] }} + radius="md" + p="xl" + > + + + + + + Seret gambar atau klik untuk memilih file + Maksimal 5MB, format gambar wajib + + + - - - Drag images here or click to select files - - - Attach as many files as you like, each file should not - exceed 5mb - - - - - - {previewImage && ( + {previewImage && ( + - )} - + + )} + {/* Tanggal Masuk */} updateForm('tanggalMasuk', e.target.value)} + onChange={(e) => setFormData({ ...formData, tanggalMasuk: e.target.value })} /> + {/* Email */} updateForm('email', e.currentTarget.value)} + onChange={(e) => setFormData({ ...formData, email: e.target.value })} /> + {/* Telepon */} updateForm('telepon', e.currentTarget.value)} + onChange={(e) => setFormData({ ...formData, telepon: e.target.value })} /> + {/* Alamat */} updateForm('alamat', e.currentTarget.value)} + onChange={(e) => setFormData({ ...formData, alamat: e.target.value })} /> - ({ - value: p.id, - label: p.nama, - }) - ) || [] - } - value={formData.posisiId} - onChange={(value) => { - if (value !== null) updateForm('posisiId', value); - }} - /> + {/* Posisi */} + + Posisi + ({ value: p.id, label: p.nama })) || []} + value={formData.posisiId} + onChange={(value) => value && setFormData({ ...formData, posisiId: value })} + searchable + clearable + /> + - updateForm('isActive', val === 'true')} - /> + {/* Status Pegawai */} + + Status Pegawai + setFormData({ ...formData, isActive: val === 'true' })} + clearable + /> + - - + {/* Submit Button */} + + Simpan diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/page.tsx index ea1f9f81..7a0626e4 100644 --- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/page.tsx @@ -1,42 +1,56 @@ 'use client' import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus'; -import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; +import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; + import colors from '@/con/colors'; -import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { Box, Button, Group, Image, Paper, Skeleton, Stack, Text, Tooltip } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; -import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { IconArrowBack, IconEdit, IconTrash } from '@tabler/icons-react'; import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; import { useProxy } from 'valtio/utils'; function DetailPegawai() { - const statePegawai = useProxy(strukturorganisasiState.pegawai) - const [modalHapus, setModalHapus] = useState(false) - const [selectedId, setSelectedId] = useState(null) - const params = useParams() + const statePegawai = useProxy(stateStrukturBumDes.pegawai); + const [modalHapus, setModalHapus] = useState(false); + const [modalNonActive, setModalNonActive] = useState(false); + const [selectedId, setSelectedId] = useState(null); + const params = useParams(); const router = useRouter(); useShallowEffect(() => { - statePegawai.findUnique.load(params?.id as string) - }, []) + stateStrukturBumDes.posisiOrganisasi.findMany.load(); + statePegawai.findUnique.load(params?.id as string); + }, []); const handleHapus = () => { if (selectedId) { - statePegawai.delete.byId(selectedId) - setModalHapus(false) - setSelectedId(null) - router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai") + statePegawai.delete.byId(selectedId); + setModalHapus(false); + setSelectedId(null); + router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai"); } - } + }; + + const handleNonActive = () => { + if (selectedId) { + statePegawai.nonActive.byId(selectedId); + setModalNonActive(false); + setSelectedId(null); + router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai"); + } + }; if (!statePegawai.findUnique.data) { return ( - + - ) + ); } + const data = statePegawai.findUnique.data; + return ( @@ -44,91 +58,125 @@ function DetailPegawai() { - - - Detail Pegawai - - + + + + Detail Pegawai PPID + + + + - Nama Lengkap - {statePegawai.findUnique.data?.namaLengkap} - - - Gelar Akademik - {statePegawai.findUnique.data?.gelarAkademik} - - - Image - {statePegawai.findUnique.data?.image?.link ? ( - - ) : ( - Tidak ada gambar - )} - - - Tanggal Masuk - - {statePegawai.findUnique.data?.tanggalMasuk - ? new Date(statePegawai.findUnique.data.tanggalMasuk).toLocaleDateString() - : "-"} + Nama Lengkap + + {data.namaLengkap || '-'} {data.gelarAkademik || ''} - - Email - {statePegawai.findUnique.data?.email} - - - Telepon - {statePegawai.findUnique.data?.telepon} - - - Alamat - {statePegawai.findUnique.data?.alamat} - - - Posisi - - {statePegawai.findUnique.data?.posisi ? ( - - {statePegawai.findUnique.data.posisi.nama} - - ) : ( - - Tidak ada posisi - - )} - - - - Aktif - {statePegawai.findUnique.data?.isActive ? "Ya" : "Tidak"} - - - { - if (statePegawai.findUnique.data) { - setSelectedId(statePegawai.findUnique.data.id); - setModalHapus(true); - } + Posisi + {data.posisi?.nama || '-'} + + + + Email + {data.email || '-'} + + + + Telepon + {data.telepon || '-'} + + + + Alamat + {data.alamat || '-'} + + + + Tanggal Masuk + + {data.tanggalMasuk ? new Date(data.tanggalMasuk).toLocaleDateString() : '-'} + + + + + Status + + {data.isActive ? 'Aktif' : 'Tidak Aktif'} + + + + + Foto Profil + {data.image?.link ? ( + - + loading='lazy' + /> + ) : ( + Tidak ada foto profil + )} + + + { + setSelectedId(data.id || null); + setModalNonActive(true); + }} + variant="light" + radius="md" + size="md" + > + {data.isActive ? "Aktif" : "Nonaktif"} + + + + + { + setSelectedId(data.id || null); + setModalHapus(true); + }} + variant="light" + radius="md" + size="md" + > + + + + { - if (statePegawai.findUnique.data) { - router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/${statePegawai.findUnique.data.id}/edit`); - } - }} - disabled={!statePegawai.findUnique.data} - color="green"> + color="green" + onClick={() => router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/${data.id}/edit`)} + variant="light" + radius="md" + size="md" + > - - + + @@ -139,7 +187,15 @@ function DetailPegawai() { opened={modalHapus} onClose={() => setModalHapus(false)} onConfirm={handleHapus} - text="Apakah anda yakin ingin menghapus produk ini?" + text="Apakah Anda yakin ingin menghapus data pegawai ini?" + /> + + {/* Modal NonActive */} + setModalNonActive(false)} + onConfirm={handleNonActive} + text="Apakah Anda yakin ingin menonaktifkan pegawai ini?" /> ); diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create/page.tsx index 4f1be2d5..dbf697ee 100644 --- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create/page.tsx @@ -1,9 +1,10 @@ /* eslint-disable react-hooks/exhaustive-deps */ 'use client' -import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; + +import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; import colors from '@/con/colors'; import ApiFetch from '@/lib/api-fetch'; -import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core'; +import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title, Tooltip } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; @@ -11,17 +12,17 @@ import { useEffect, useState } from 'react'; import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; -function CreatePegawai() { +function CreatePegawaiBumDes() { const router = useRouter(); const [previewImage, setPreviewImage] = useState<{ preview: string; file: File } | null>(null); - const stateOrganisasi = useProxy(strukturorganisasiState) + const stateOrganisasi = useProxy(stateStrukturBumDes.pegawai) useEffect(() => { - stateOrganisasi.posisiOrganisasi.findMany.load(); + stateStrukturBumDes.posisiOrganisasi.findManyAll.load(); resetForm(); }, []); const resetForm = () => { - stateOrganisasi.pegawai.create.form = { + stateOrganisasi.create.form = { namaLengkap: "", gelarAkademik: "", imageId: "", @@ -30,7 +31,7 @@ function CreatePegawai() { telepon: "", alamat: "", posisiId: "", - isActive: true, + isActive: true, }; }; @@ -52,15 +53,15 @@ function CreatePegawai() { } // Set status aktif secara otomatis - stateOrganisasi.pegawai.create.form.isActive = true; - + stateOrganisasi.create.form.isActive = true; + // Simpan ID gambar ke form - stateOrganisasi.pegawai.create.form.imageId = uploaded.id; - + stateOrganisasi.create.form.imageId = uploaded.id; + // Submit form - await stateOrganisasi.pegawai.create.submit(); - - + await stateOrganisasi.create.submit(); + + // Reset form dan redirect resetForm(); toast.success("Data pegawai berhasil ditambahkan"); @@ -72,130 +73,194 @@ function CreatePegawai() { }; return ( - - - router.back()}> - - - - - - Create Pegawai - (stateOrganisasi.pegawai.create.form.namaLengkap = e.currentTarget.value)} - /> - (stateOrganisasi.pegawai.create.form.gelarAkademik = e.currentTarget.value)} - /> - - Gambar - - { - const file = files[0]; // Hanya ambil file pertama - if (file) { - setPreviewImage({ - file, - preview: URL.createObjectURL(file) - }); - } - }} - maxSize={5 * 1024 ** 2} // 5MB - accept={{ - 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] - }} - > - - - - - - - - - - + + + + router.back()} p="xs" radius="md"> + + + + + Tambah Pegawai BUMDesa + + - - - Drag images here or click to select files - - - Attach as many files as you like, each file should not exceed 5mb - - - - - {previewImage && ( + + + + (stateOrganisasi.create.form.namaLengkap = e.currentTarget.value)} + required + /> + + + (stateOrganisasi.create.form.gelarAkademik = e.currentTarget.value)} + /> + + + + Foto Profil + + { + const file = files[0]; + if (file) { + setPreviewImage({ + file, + preview: URL.createObjectURL(file) + }); + } + }} + maxSize={5 * 1024 ** 2} // 5MB + accept={{ + 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] + }} + styles={{ + root: { + border: '2px dashed #ced4da', + borderRadius: '8px', + padding: '20px', + textAlign: 'center', + cursor: 'pointer', + '&:hover': { + borderColor: '#228be6', + }, + }, + }} + > + + + + + + + + + + + + + + Seret gambar ke sini atau klik untuk memilih file + + + Format yang didukung: JPG, PNG, WebP. Maksimal 5MB + + + + + + {previewImage && ( + + + Preview Gambar + - )} - + + )} + + + (stateOrganisasi.create.form.tanggalMasuk = e.currentTarget.value)} + /> + + + + (stateOrganisasi.create.form.email = e.currentTarget.value)} + /> + + + + (stateOrganisasi.create.form.telepon = e.currentTarget.value)} + /> + + + + (stateOrganisasi.create.form.alamat = e.currentTarget.value)} + /> + + + + + Posisi + + ({ + value: p.id, + label: p.nama + })) || []} + value={stateOrganisasi.create.form.posisiId} + onChange={(value) => { + if (value) stateOrganisasi.create.form.posisiId = value; + }} + searchable + clearable + /> - (stateOrganisasi.pegawai.create.form.tanggalMasuk = e.currentTarget.value)} - /> - (stateOrganisasi.pegawai.create.form.email = e.currentTarget.value)} - /> - (stateOrganisasi.pegawai.create.form.telepon = e.currentTarget.value)} - /> - (stateOrganisasi.pegawai.create.form.alamat = e.currentTarget.value)} - /> - ({ - value: p.id, - label: p.nama - })) || []} - defaultValue={stateOrganisasi.pegawai.create.form.posisiId} - onChange={(value) => { - if (value) stateOrganisasi.pegawai.create.form.posisiId = value; - }} - searchable - /> - - Simpan - + + + Simpan + + ); } -export default CreatePegawai; +export default CreatePegawaiBumDes; diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/page.tsx index 2a140fc7..d56609de 100644 --- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/page.tsx @@ -1,33 +1,33 @@ /* eslint-disable react-hooks/exhaustive-deps */ 'use client' import colors from '@/con/colors'; -import { Badge, Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, ThemeIcon } from '@mantine/core'; -import { IconCheck, IconDeviceImacCog, IconSearch, IconX } from '@tabler/icons-react'; +import { Badge, Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, ThemeIcon, Title, Tooltip } from '@mantine/core'; +import { IconCheck, IconDeviceImacCog, IconPlus, IconSearch, IconX } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; -import { useEffect, useMemo, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useProxy } from 'valtio/utils'; import HeaderSearch from '../../../_com/header'; -import JudulList from '../../../_com/judulList'; -import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi'; +import stateStrukturBumDes from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi'; -function Pegawai() { + +function PegawaiBumDes() { const [search, setSearch] = useState(""); return ( } value={search} onChange={(e) => setSearch(e.currentTarget.value)} /> - + ); } -function ListPegawai({ search }: { search: string }) { - const stateOrganisasi = useProxy(strukturorganisasiState.pegawai); +function ListPegawaiBumdes({ search }: { search: string }) { + const stateOrganisasi = useProxy(stateStrukturBumDes.pegawai); const router = useRouter(); const { @@ -39,21 +39,10 @@ function ListPegawai({ search }: { search: string }) { } = stateOrganisasi.findMany; useEffect(() => { - load(page, 10); - }, [page]); + load(page, 10, search); + }, [page, search]); - const filteredData = useMemo(() => { - if (!data) return []; - return data.filter(item => { - const keyword = search.toLowerCase(); - return ( - item.namaLengkap?.toLowerCase().includes(keyword) || - item.gelarAkademik?.toLowerCase().includes(keyword) || - item.telepon?.toLowerCase().includes(keyword) || - item.posisi?.nama?.toLowerCase().includes(keyword) - ); - }); - }, [data, search]); + const filteredData = data || [] // Handle loading state if (loading || !data) { @@ -67,29 +56,51 @@ function ListPegawai({ search }: { search: string }) { if (data.length === 0) { return ( - - Tidak ada data pegawai yang tersedia + + + Daftar Pegawai BUMDesa + + } + color="blue" + variant="light" + onClick={() => router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create')} + > + Tambah Baru + + + + + Tidak ada data pegawai yang ditemukan + ); } return ( - - + + + Daftar Pegawai BUMDesa + + } + color="blue" + variant="light" + onClick={() => router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create')} + > + Tambah Baru + + + - + - Nama - Gelar Akademik - Telepon - Posisi - Aktif - Detail + Nama Lengkap + Posisi + Status + Aksi @@ -106,10 +117,20 @@ function ListPegawai({ search }: { search: string }) { }) // Aktif di atas ).map((item) => ( - {item.namaLengkap} - {item.gelarAkademik} - {item.telepon} - {item.posisi?.nama} + + + + {item.namaLengkap} + + + + + + + {item.posisi?.nama || 'Belum diatur'} + + + @@ -131,8 +152,15 @@ function ListPegawai({ search }: { search: string }) { - router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/${item.id}`)}> - + } + onClick={() => router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/${item.id}`)} + > + Detail @@ -140,21 +168,22 @@ function ListPegawai({ search }: { search: string }) { + + { + load(newPage, 10); + window.scrollTo(0, 0); + }} + total={totalPages} + withEdges + withControls + radius="md" + /> + - - { - load(newPage, 10); - window.scrollTo(0, 0); - }} - total={totalPages} - mt="md" - mb="md" - /> - ); } -export default Pegawai; +export default PegawaiBumDes; diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/[id]/page.tsx index b43051cb..0d8f7ed7 100644 --- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/[id]/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/[id]/page.tsx @@ -1,30 +1,22 @@ +/* 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 strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; +import stateStrukturBumDes from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; import colors from '@/con/colors'; -import { - Box, - Button, - Group, - Paper, - Stack, - Text, - TextInput, - Title, - Tooltip, -} from '@mantine/core'; +import { Box, Button, Group, Paper, Stack, Text, TextInput, Title, Tooltip } 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 EditPosisiOrganisasi() { +function EditPosisiOrganisasiBumDes() { const router = useRouter(); const params = useParams(); const id = params?.id as string; - const stateOrganisasi = useProxy(strukturorganisasiState.posisiOrganisasi); + const stateOrganisasi = useProxy(stateStrukturBumDes.posisiOrganisasi); const [formData, setFormData] = useState({ nama: '', @@ -32,24 +24,27 @@ function EditPosisiOrganisasi() { hierarki: 0, }); - // Load data awal sekali saja - useEffect(() => { - const loadPosisiOrganisasi = async () => { - if (!id) return; + // Fungsi generik untuk update formData + const handleChange = (field: keyof typeof formData, value: any) => { + setFormData(prev => ({ ...prev, [field]: value })); + }; + useEffect(() => { + if (!id) return; + + const loadPosisiOrganisasi = async () => { try { const data = await stateOrganisasi.edit.load(id); - if (data) { stateOrganisasi.edit.id = id; setFormData({ - nama: data.nama ?? '', - deskripsi: data.deskripsi ?? '', - hierarki: data.hierarki ?? 0, + nama: data.nama || '', + deskripsi: data.deskripsi || '', + hierarki: data.hierarki || 0, }); } - } catch (error) { - console.error('Error loading posisi organisasi:', error); + } catch (err) { + console.error('Error loading posisi organisasi:', err); toast.error('Gagal memuat data posisi organisasi'); } }; @@ -57,18 +52,14 @@ function EditPosisiOrganisasi() { loadPosisiOrganisasi(); }, [id]); - const handleChange = (field: string, value: string | number) => { - setFormData((prev) => ({ ...prev, [field]: value })); - }; - const handleSubmit = async () => { - try { - if (!formData.nama.trim()) { - toast.error('Nama posisi organisasi tidak boleh kosong'); - return; - } + if (!formData.nama.trim()) { + toast.error('Nama posisi organisasi tidak boleh kosong'); + return; + } - // update global state HANYA saat submit + try { + // Update global state hanya saat submit stateOrganisasi.edit.form = { nama: formData.nama.trim(), deskripsi: formData.deskripsi.trim(), @@ -76,19 +67,17 @@ function EditPosisiOrganisasi() { }; if (!stateOrganisasi.edit.id) { - stateOrganisasi.edit.id = id; // fallback + stateOrganisasi.edit.id = id; } const success = await stateOrganisasi.edit.update(); if (success) { - toast.success('Posisi organisasi berhasil diperbarui!'); - router.push( - '/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi' - ); + router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi'); } - } catch (error) { - console.error('Error updating posisi organisasi:', error); + } catch (err) { + console.error('Error updating posisi organisasi:', err); + // toast error biasanya sudah ada di update } }; @@ -96,17 +85,12 @@ function EditPosisiOrganisasi() { - router.back()} - p="xs" - radius="md" - > + router.back()} p="xs" radius="md"> - Edit Posisi Organisasi + Edit Posisi Organisasi BUMDes @@ -120,39 +104,40 @@ function EditPosisiOrganisasi() { > handleChange('nama', e.target.value)} label="Nama Posisi Organisasi" placeholder="Masukkan nama posisi organisasi" + value={formData.nama} + onChange={(e) => handleChange('nama', e.target.value)} required /> - + Deskripsi - handleChange('deskripsi', htmlContent) - } + onChange={(html) => handleChange('deskripsi', html)} /> - handleChange('hierarki', parseInt(e.target.value) || 0) - } label="Hierarki" - placeholder="Masukkan hierarki" + type="number" + min={0} + placeholder="Contoh: 1 (Angka semakin kecil, posisi semakin tinggi)" + value={formData.hierarki} + onChange={(e) => { + const value = parseInt(e.target.value, 10); + handleChange('hierarki', isNaN(value) ? 0 : value); + }} required /> - + { - stateOrganisasi.findMany.load(); - }, []); - - const resetForm = () => { - stateOrganisasi.create.form = { - nama: '', - deskripsi: '', - hierarki: 0, - }; +function CreatePosisiOrganisasiBumDes() { + const router = useRouter(); + const stateOrganisasi = useProxy(stateStrukturBumDes.posisiOrganisasi); + + useEffect(() => { + stateOrganisasi.findMany.load(); + // Initialize form with default values + stateOrganisasi.create.form = { + nama: "", + deskripsi: "", + hierarki: 0, }; - - const handleSubmit = async () => { - await stateOrganisasi.create.submit(); - resetForm(); - router.push( - '/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi' - ); + + return () => { + // Clean up form on unmount + stateOrganisasi.create.form = { + nama: "", + deskripsi: "", + hierarki: 0, + }; }; + }, []); - return ( - - {/* Header Back + Title */} - - - router.back()} - p="xs" - radius="md" - > - - - - - Tambah Posisi Organisasi - - + const handleSubmit = async () => { + try { + if (!stateOrganisasi.create.form.nama.trim()) { + return toast.error('Nama posisi tidak boleh kosong'); + } + + await stateOrganisasi.create.submit(); + toast.success('Posisi organisasi berhasil ditambahkan'); + router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi'); + } catch (error) { + toast.error('Gagal menambahkan posisi organisasi'); + console.error('Error:', error); + } + }; - {/* Form Card */} - + + + router.back()} p="xs" radius="md"> + + + + + Tambah Posisi Organisasi BUMDes + + + + + + (stateOrganisasi.create.form.nama = e.target.value)} + required + /> + + + + Deskripsi + + { + stateOrganisasi.create.form.deskripsi = htmlContent; + }} + /> + + + { + const value = parseInt(e.target.value, 10); + stateOrganisasi.create.form.hierarki = isNaN(value) ? 0 : value; + }} + required + /> + + + - - (stateOrganisasi.create.form.nama = e.currentTarget.value)} - required - /> - - - Deskripsi - { - stateOrganisasi.create.form.deskripsi = htmlContent; - }} - /> - - - { - const value = parseInt(e.currentTarget.value, 10); - if (!isNaN(value)) { - stateOrganisasi.create.form.hierarki = value; - } - }} - required - /> - - {/* Action Button */} - - - Simpan - - - - - - ); + Simpan + + + + + + ); } -export default CreatePosisiOrganisasi; +export default CreatePosisiOrganisasiBumDes; diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/page.tsx index 33f0cfd0..ea181a8f 100644 --- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/page.tsx @@ -1,58 +1,37 @@ 'use client' import colors from '@/con/colors'; -import { - Box, - Button, - Center, - Group, - Paper, - Pagination, - Skeleton, - Stack, - Table, - TableTbody, - TableTd, - TableTh, - TableThead, - TableTr, - Text, - Title, - Tooltip, -} from '@mantine/core'; +import { Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, Tooltip } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; -import { useShallowEffect } from '@mantine/hooks'; import { useState } from 'react'; import { useProxy } from 'valtio/utils'; import HeaderSearch from '../../../_com/header'; import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; -import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi'; +import stateStrukturBumDes from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi'; -function PosisiOrganisasi() { + +function PosisiOrganisasiBumDes() { const [search, setSearch] = useState(""); return ( - {/* Search Bar */} } value={search} onChange={(e) => setSearch(e.currentTarget.value)} /> - - {/* List Table */} - + ); } -function ListPosisiOrganisasi({ search }: { search: string }) { - const stateOrganisasi = useProxy(strukturorganisasiState.posisiOrganisasi); +function ListPosisiOrganisasiBumDes({ search }: { search: string }) { + const stateOrganisasi = useProxy(stateStrukturBumDes.posisiOrganisasi) const router = useRouter(); - - const [modalHapus, setModalHapus] = useState(false); - const [selectedId, setSelectedId] = useState(null); + const [modalHapus, setModalHapus] = useState(false) + const [selectedId, setSelectedId] = useState(null) const { data, @@ -66,21 +45,20 @@ function ListPosisiOrganisasi({ search }: { search: string }) { load(page, 10, search); }, [page, search]); - const filteredData = data || []; - const handleHapus = async () => { if (selectedId) { await stateOrganisasi.delete.byId(selectedId); - setModalHapus(false); - setSelectedId(null); - load(page, 10, search); // refresh + setModalHapus(false) + setSelectedId(null) } - }; + } + + const filteredData = data || [] if (loading || !data) { return ( - + ); } @@ -89,71 +67,70 @@ function ListPosisiOrganisasi({ search }: { search: string }) { - Daftar Posisi Organisasi - + Daftar Posisi Organisasi BumDes + } color="blue" variant="light" - onClick={() => - router.push( - '/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/create' - ) - } + onClick={() => router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/create')} > Tambah Baru - - Nama Posisi - Hierarki - Edit - Hapus + Nama Posisi + Deskripsi + Hierarki + Edit + Hapus {filteredData.length > 0 ? ( filteredData.map((item) => ( - + {item.nama} - - {item.hierarki ?? '-'} + + + + - - + + {item.hierarki || '-'} + + + - router.push( - `/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/${item.id}` - ) - } + size="sm" + onClick={() => router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/${item.id}`)} > - - - { - setSelectedId(item.id); - setModalHapus(true); - }} - > - - - + + + { + setSelectedId(item.id); + setModalHapus(true); + }} + > + + + )) @@ -170,8 +147,6 @@ function ListPosisiOrganisasi({ search }: { search: string }) { - - {/* Pagination */} - {/* Modal Hapus */} setModalHapus(false)} onConfirm={handleHapus} - text="Apakah anda yakin ingin menghapus posisi organisasi ini?" + text="Apakah anda yakin ingin menghapus posisi organisasi BumDes ini?" /> ); } -export default PosisiOrganisasi; +export default PosisiOrganisasiBumDes; diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/struktur-organisasi/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/struktur-organisasi/page.tsx new file mode 100644 index 00000000..be24d78b --- /dev/null +++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/struktur-organisasi/page.tsx @@ -0,0 +1,133 @@ +/* eslint-disable prefer-const */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import { Box, Center, Image, Loader, Paper, Stack, Text, Tooltip } from '@mantine/core'; +import { IconUsers } from '@tabler/icons-react'; +import { OrganizationChart } from 'primereact/organizationchart'; +import { useEffect } from 'react'; +import { useProxy } from 'valtio/utils'; +import stateStrukturBumDes from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi'; + +function StrukturOrganisasiBumDes() { + return ( + + + + ); +} + +function ListStrukturOrganisasiBumDes() { + const stateOrganisasi = useProxy(stateStrukturBumDes.pegawai); + + useEffect(() => { + stateOrganisasi.findMany.load(); + }, []); + + if (stateOrganisasi.findMany.loading) { + return ( + + + + ); + } + + if (!stateOrganisasi.findMany.data || stateOrganisasi.findMany.data.length === 0) { + return ( + + + Belum ada struktur organisasi yang ditambahkan + + ); + } + + const posisiMap = new Map(); + + const aktifPegawai = stateOrganisasi.findMany.data.filter(p => p.isActive); + + for (const pegawai of aktifPegawai) { + const posisiId = pegawai.posisi.id; + if (!posisiMap.has(posisiId)) { + posisiMap.set(posisiId, { + ...pegawai.posisi, + pegawaiList: [], + children: [], + }); + } + posisiMap.get(posisiId)!.pegawaiList.push(pegawai); + } + + let root: any[] = []; + posisiMap.forEach((posisi) => { + if (posisi.parentId) { + const parent = posisiMap.get(posisi.parentId); + if (parent) { + parent.children.push(posisi); + } + } else { + root.push(posisi); + } + }); + + function toOrgChartFormat(node: any): any { + return { + expanded: true, + type: 'person', + styleClass: 'p-person', + data: { + name: node.pegawaiList?.[0]?.namaLengkap || 'Belum ada pegawai', + status: node.nama, + image: node.pegawaiList?.[0]?.image?.link || '/img/default.png', + }, + children: node.children.map(toOrgChartFormat), + }; + } + + const chartData = root.map(toOrgChartFormat); + + return ( + + + + + + ); +} + +function nodeTemplate(node: any) { + const imageSrc = node?.data?.image || '/img/default.png'; + const name = node?.data?.name || 'Tanpa Nama'; + const status = node?.data?.status || 'Tidak ada deskripsi'; + + return ( + + + + + {name} + {status} + + ); +} + +export default StrukturOrganisasiBumDes; diff --git a/src/app/admin/_com/list_PageAdmin.tsx b/src/app/admin/_com/list_PageAdmin.tsx index 64a808f0..673fda1d 100644 --- a/src/app/admin/_com/list_PageAdmin.tsx +++ b/src/app/admin/_com/list_PageAdmin.tsx @@ -225,7 +225,7 @@ export const navBar = [ { id: "Ekonomi_3", name: "Struktur Organisasi Dan Sk Pengurus Bumdesa", - path: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi" + path: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai" }, { id: "Ekonomi_4", diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/index.ts index 2fb84a21..05e511c6 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/index.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/index.ts @@ -3,7 +3,6 @@ import PasarDesa from "./pasar-desa"; import LowonganKerja from "./lowongan-kerja"; import ProgramKemiskinan from "./program-kemiskinan"; import KategoriProduk from "./pasar-desa/kategori-produk"; -import StrukturOrganisasi from "./struktur-organisasi"; import GrafikUsiaKerjaYangMenganggur from "./usia-kerja-yang-menganggur"; import GrafikMenganggurBerdasarkanPendidikan from "./usia-kerja-yang-menganggur/pengangguran-berdasrkan-pendidikan"; import JumlahPendudukMiskin from "./jumlah-penduduk-miskin"; @@ -11,6 +10,7 @@ import SektorUnggulanDesa from "./sektor-unggulan-desa"; import DemografiPekerjaan from "./demografi-pekerjaan"; import JumlahPengangguran from "./jumlah-pengangguran"; import PendapatanAsliDesa from "./pendapatan-asli-desa"; +import StrukturOrganisasi from "./struktur-bumdes"; const Ekonomi = new Elysia({ prefix: "/api/ekonomi", diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/find-by-id.ts new file mode 100644 index 00000000..b88c1c62 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/find-by-id.ts @@ -0,0 +1,51 @@ +import prisma from "@/lib/prisma"; + +export default async function strukturBumDesFindById(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.strukturBumDes.findUnique({ + where: { id }, + include: { + image: true, + } + }); + + 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 (e) { + console.error("Find by ID error:", e); + return Response.json({ + success: false, + message: "Gagal mengambil data: " + (e instanceof Error ? e.message : 'Unknown error'), + }, { + status: 500, + }); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/index.ts new file mode 100644 index 00000000..4317d3bd --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/index.ts @@ -0,0 +1,29 @@ +import Elysia, { t } from "elysia"; +import PosisiOrganisasi from "./posisi-organisasi"; +import Pegawai from "./pegawai"; +import strukturBumDesFindById from "./find-by-id"; +import strukturBumDesUpdate from "./update"; + + +const StrukturOrganisasi = new Elysia({ + prefix: "/struktur-organisasi", + tags: ["Ekonomi/Struktur Organisasi"], +}) +.get("/:id", async (context) => { + const response = await strukturBumDesFindById(new Request(context.request)) + return response +}) +.use(PosisiOrganisasi) +.use(Pegawai) +.put("/:id", async (context) => { + const response = await strukturBumDesUpdate(context) + return response +}, { + body: t.Object({ + name: t.String(), + imageId: t.String(), + }) +}) + + +export default StrukturOrganisasi; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/create.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/create.ts similarity index 97% rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/create.ts rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/create.ts index 824771db..8619492c 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/create.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/create.ts @@ -32,7 +32,7 @@ export default async function pegawaiCreate(context: Context) { } try { - const pegawai = await prisma.pegawai.create({ + const pegawai = await prisma.pegawaiBumDes.create({ data: { namaLengkap: body.namaLengkap, gelarAkademik: body.gelarAkademik, diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/del.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/del.ts similarity index 74% rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/del.ts rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/del.ts index a2b3b7de..52a204fd 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/del.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/del.ts @@ -13,17 +13,13 @@ export default async function pegawaiDelete(context: Context) { } try { - const deleted = await prisma.pegawai.update({ - where: { id }, - data: { - isActive: false, // soft delete - updatedAt: new Date(), - }, + const deleted = await prisma.pegawaiBumDes.delete({ + where: { id }, }); return { success: true, - message: "Pegawai berhasil di-nonaktifkan", + message: "Pegawai berhasil dihapus", data: deleted, }; } catch (error: any) { diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findMany.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findMany.ts similarity index 93% rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findMany.ts rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findMany.ts index b5bf9570..aa7a6e53 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findMany.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findMany.ts @@ -9,7 +9,7 @@ export default async function pegawaiFindMany(context: Context) { try { const [data, total] = await Promise.all([ - prisma.pegawai.findMany({ + prisma.pegawaiBumDes.findMany({ where: { isActive: true }, include: { posisi: true, @@ -19,7 +19,7 @@ export default async function pegawaiFindMany(context: Context) { take: limit, orderBy: { createdAt: 'desc' }, }), - prisma.pegawai.count({ + prisma.pegawaiBumDes.count({ where: { isActive: true } }) ]); diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findUnique.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findUnique.ts similarity index 93% rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findUnique.ts rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findUnique.ts index 4c2c4884..8b00abd6 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findUnique.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findUnique.ts @@ -13,7 +13,7 @@ export default async function pegawaiFindUnique(context: Context) { } try { - const pegawai = await prisma.pegawai.findUnique({ + const pegawai = await prisma.pegawaiBumDes.findUnique({ where: { id }, include: { posisi: true, diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/index.ts similarity index 96% rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/index.ts rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/index.ts index cb969630..cecf97cc 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/index.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/index.ts @@ -7,7 +7,7 @@ import pegawaiUpdate from "./updt"; const Pegawai = new Elysia({ prefix: "/pegawai", - tags: ["Ekonomi/Struktur Organisasi/Pegawai"], + tags: ["Ekonomi/Struktur BUMDes/Pegawai"], }) // ✅ Find all diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/updt.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/updt.ts similarity index 96% rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/updt.ts rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/updt.ts index 4f9b54fa..b7f8929f 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/updt.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/updt.ts @@ -28,7 +28,7 @@ export default async function pegawaiUpdate(context: Context) { body.isActive = String(body.isActive).toLowerCase() === 'true'; try { - const updated = await prisma.pegawai.update({ + const updated = await prisma.pegawaiBumDes.update({ where: { id: body.id }, data: { namaLengkap: body.namaLengkap, diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/create.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/create.ts similarity index 92% rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/create.ts rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/create.ts index 10b3a994..69aa2578 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/create.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/create.ts @@ -18,7 +18,7 @@ export default async function posisiOrganisasiCreate(context: Context) { } try { - const posisiOrganisasi = await prisma.posisiOrganisasi.create({ + const posisiOrganisasi = await prisma.posisiOrganisasiBumDes.create({ data: { nama: body.nama, deskripsi: body.deskripsi, diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/del.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/del.ts similarity index 70% rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/del.ts rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/del.ts index 0560b115..07373122 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/del.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/del.ts @@ -16,7 +16,7 @@ export default async function posisiOrganisasiDelete(context: Context) { try { // Check if the position exists first - const existing = await prisma.posisiOrganisasi.findUnique({ + const existing = await prisma.posisiOrganisasiBumDes.findUnique({ where: { id }, }); @@ -31,7 +31,7 @@ export default async function posisiOrganisasiDelete(context: Context) { } // Check if there are any pegawai associated with this position - const pegawaiCount = await prisma.pegawai.count({ + const pegawaiCount = await prisma.pegawaiBumDes.count({ where: { posisiId: id }, }); @@ -45,28 +45,8 @@ export default async function posisiOrganisasiDelete(context: Context) { ); } - // Check if this position is used in any hubungan organisasi - const hubunganCount = await prisma.hubunganOrganisasi.count({ - where: { - OR: [ - { atasanId: id }, - { bawahanId: id }, - ], - }, - }); - - if (hubunganCount > 0) { - return new Response( - JSON.stringify({ - success: false, - message: "Tidak dapat menghapus posisi yang masih terdaftar dalam struktur organisasi", - }), - { status: 400, headers: { "Content-Type": "application/json" } } - ); - } - // If all checks pass, delete the position - const deleted = await prisma.posisiOrganisasi.delete({ + const deleted = await prisma.posisiOrganisasiBumDes.delete({ where: { id }, }); diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/findMany.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findMany.ts similarity index 93% rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/findMany.ts rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findMany.ts index ea81b455..bef2f638 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/findMany.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findMany.ts @@ -23,13 +23,13 @@ export default async function posisiOrganisasiFindMany(context: Context) { try { // Ambil data dan total count secara paralel const [data, total] = await Promise.all([ - prisma.posisiOrganisasi.findMany({ + prisma.posisiOrganisasiBumDes.findMany({ where, skip, take: limit, orderBy: { createdAt: "asc" }, }), - prisma.posisiOrganisasi.count({ where }), + prisma.posisiOrganisasiBumDes.count({ where }), ]); return { diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findManyAll.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findManyAll.ts new file mode 100644 index 00000000..a41d5c73 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findManyAll.ts @@ -0,0 +1,45 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// /api/posisi-organisasi/findManyAll.ts +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +async function posisiOrganisasiFindManyAll(context: Context) { + const search = (context.query.search as string) || ""; + + // filter default + const where: any = { isActive: true }; + + if (search) { + where.OR = [ + { nama: { contains: search, mode: "insensitive" } }, + { deskripsi: { contains: search, mode: "insensitive" } }, + ]; + } + + try { + const data = await prisma.posisiOrganisasiPPID.findMany({ + where, + orderBy: { hierarki: "asc" }, + }); + + return { + success: true, + message: "Berhasil mengambil semua data posisi organisasi", + data: data.map((item: any) => ({ + id: item.id, + nama: item.nama, + deskripsi: item.deskripsi, + hierarki: item.hierarki, + })), + total: data.length, + }; + } catch (e) { + console.error("Find many all error:", e); + return { + success: false, + message: "Gagal mengambil data posisi organisasi", + }; + } +} + +export default posisiOrganisasiFindManyAll; diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/findUnique.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findUnique.ts similarity index 94% rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/findUnique.ts rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findUnique.ts index 13092469..eefd821d 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/findUnique.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/findUnique.ts @@ -21,7 +21,7 @@ export default async function posisiOrganisasiFindUnique(context: Context) { } } - const data = await prisma.posisiOrganisasi.findUnique({ + const data = await prisma.posisiOrganisasiBumDes.findUnique({ where: { id }, }); diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/index.ts similarity index 90% rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/index.ts rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/index.ts index 4338204d..eba36b3e 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/index.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/index.ts @@ -4,6 +4,7 @@ import posisiOrganisasiFindUnique from "./findUnique"; import posisiOrganisasiCreate from "./create"; import posisiOrganisasiUpdate from "./updt"; import posisiOrganisasiDelete from "./del"; +import posisiOrganisasiFindManyAll from "./findManyAll"; const PosisiOrganisasi = new Elysia({ prefix: "/posisi-organisasi", @@ -11,6 +12,7 @@ const PosisiOrganisasi = new Elysia({ }) .get("/find-many", posisiOrganisasiFindMany) +.get("/find-many-all", posisiOrganisasiFindManyAll) .get("/:id", async (context) => { const response = await posisiOrganisasiFindUnique(context); return response; diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/updt.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/updt.ts similarity index 88% rename from src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/updt.ts rename to src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/updt.ts index e3639460..c5768081 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/posisi-organisasi/updt.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/posisi-organisasi/updt.ts @@ -20,7 +20,7 @@ export default async function posisiOrganisasiUpdate(context: Context) { } try { - await prisma.posisiOrganisasi.update({ + await prisma.posisiOrganisasiBumDes.update({ where: { id }, data: { nama: body.nama, @@ -29,7 +29,7 @@ export default async function posisiOrganisasiUpdate(context: Context) { }, }); - const updated = await prisma.posisiOrganisasi.findUnique({ + const updated = await prisma.posisiOrganisasiBumDes.findUnique({ where: { id }, }); diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/update.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/update.ts new file mode 100644 index 00000000..e8991b81 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/update.ts @@ -0,0 +1,116 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; +import path from "path"; +import fs from "fs/promises"; +import { Prisma } from "@prisma/client"; + +type FormUpdate = Prisma.StrukturBumDesGetPayload<{ + select: { + id: true; + name: true; + imageId: true; + }; +}>; + +export default async function strukturBumDesUpdate(context: Context) { +try { + const id = context.params?.id as string; + const body = (await context.body) as Omit; + + const { name, imageId } = body; + + if (!id) { + return new Response( + JSON.stringify({ + success: false, + message: "ID tidak boleh kosong", + }), + { + status: 400, + headers: { + "Content-Type": "application/json", + }, + } + ) + } + + const existing = await prisma.strukturBumDes.findUnique({ + where: { + id + }, + include: { + image: true, + } + }) + + if (!existing) { + return new Response( + JSON.stringify({ + success: false, + message: "Data tidak ditemukan", + }), + { + status: 404, + headers: { + "Content-Type": "application/json", + }, + } + ) + } + + if (existing.imageId !== imageId) { + const oldImage = existing.image; + if (oldImage) { + try { + const filePath = path.join(oldImage.path, oldImage.name); + await fs.unlink(filePath); + await prisma.fileStorage.delete({ + where: { id: oldImage.id }, + }) + } catch (error) { + console.error("Gagal hapus gambar lama:", error); + } + } + } + + const updated = await prisma.strukturBumDes.update({ + where: { + id + }, + data: { + name, + imageId, + } + }) + + return new Response( + JSON.stringify({ + success: true, + message: "Struktur BumDes Berhasil Dibuat", + data: updated, + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ) + +} catch (error) { + console.error("Error updating struktur BumDes:", error); + return new Response( + JSON.stringify({ + success: false, + message: "Terjadi kesalahan saat mengupdate struktur BumDes", + }), + { + status: 500, + headers: { + "Content-Type": "application/json", + }, + } + ) +} +} + \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/create.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/create.ts deleted file mode 100644 index db964952..00000000 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/create.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import prisma from "@/lib/prisma"; -import { Context } from "elysia"; - -type FormCreateHubunganOrganisasi = { - atasanId: string; - bawahanId: string; - tipe?: string; -}; - -export default async function hubunganOrganisasiCreate(context: Context) { - const body = await context.body as FormCreateHubunganOrganisasi; - - // Validasi minimal - if (!body || !body.atasanId || !body.bawahanId) { - return { - success: false, - message: "atasanId dan bawahanId wajib diisi", - }; - } - - try { - const data = await prisma.hubunganOrganisasi.create({ - data: { - atasanId: body.atasanId, - bawahanId: body.bawahanId, - tipe: body.tipe, - }, - }); - - return { - success: true, - message: "Berhasil membuat hubungan organisasi", - data, - }; - } catch (error: any) { - if (error.code === "P2002") { - return { - success: false, - message: "Hubungan antara atasan dan bawahan sudah ada", - }; - } - - console.error("Error create hubungan organisasi:", error); - return { - success: false, - message: "Gagal membuat hubungan organisasi", - error: error.message, - }; - } -} diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/del.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/del.ts deleted file mode 100644 index c65bcf67..00000000 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/del.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import prisma from "@/lib/prisma"; -import { Context } from "elysia"; - -export default async function hubunganOrganisasiDelete(context: Context) { - const { id } = context.params as { id: string }; - - if (!id) { - return { - success: false, - message: "ID wajib diisi", - }; - } - - try { - const deleted = await prisma.hubunganOrganisasi.delete({ - where: { id }, - }); - - return { - success: true, - message: "Hubungan organisasi berhasil dihapus", - data: deleted, - }; - } catch (error: any) { - console.error("Error delete hubungan organisasi:", error); - return { - success: false, - message: "Gagal menghapus hubungan organisasi", - error: error.message, - }; - } -} diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/findMany.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/findMany.ts deleted file mode 100644 index 4de4200d..00000000 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/findMany.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import prisma from "@/lib/prisma"; - -export default async function hubunganOrganisasiFindMany() { - try { - const data = await prisma.hubunganOrganisasi.findMany({ - include: { - atasan: true, - bawahan: true, - }, - orderBy: { - atasanId: "asc", - }, - }); - - return { - success: true, - data, - }; - } catch (error: any) { - console.error("Error findMany hubungan organisasi:", error); - return { - success: false, - message: "Gagal mengambil data hubungan organisasi", - error: error.message, - }; - } -} diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/findUnique.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/findUnique.ts deleted file mode 100644 index d8d16cba..00000000 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/findUnique.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import prisma from "@/lib/prisma"; -import { Context } from "elysia"; - -export default async function hubunganOrganisasiFindUnique(context: Context) { - const { id } = context.params as { id: string }; - - if (!id) { - return { - success: false, - message: "ID hubungan organisasi wajib diisi", - }; - } - - try { - const data = await prisma.hubunganOrganisasi.findUnique({ - where: { id }, - include: { - atasan: true, - bawahan: true, - }, - }); - - if (!data) { - return { - success: false, - message: "Data hubungan organisasi tidak ditemukan", - }; - } - - return { - success: true, - data, - }; - } catch (error: any) { - console.error("Error findUnique hubungan organisasi:", error); - return { - success: false, - message: "Gagal mengambil data", - error: error.message, - }; - } -} diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/index.ts deleted file mode 100644 index 994bdd4a..00000000 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -import Elysia, { t } from "elysia"; -import hubunganOrganisasiFindMany from "./findMany"; -import hubunganOrganisasiFindUnique from "./findUnique"; -import hubunganOrganisasiCreate from "./create"; -import hubunganOrganisasiUpdate from "./updt"; -import hubunganOrganisasiDelete from "./del"; - - -const HubunganOrganisasi = new Elysia({ - prefix: "/hubungan-organisasi", - tags: ["Ekonomi/Struktur Organisasi/Hubungan Organisasi"], -}) - - // 🔍 GET /find-many - .get("/find-many", hubunganOrganisasiFindMany) - - // 🔍 GET /:id - .get("/:id", async (context) => { - return await hubunganOrganisasiFindUnique(context); - }) - - // ➕ POST /create - .post("/create", hubunganOrganisasiCreate, { - body: t.Object({ - atasanId: t.String(), - bawahanId: t.String(), - tipe: t.Optional(t.String()), - }), - }) - - // ✏️ PUT /:id - .put( "/:id", - async (context) => { - const response = await hubunganOrganisasiUpdate(context); - return response; - }, { - body: t.Object({ - atasanId: t.Optional(t.String()), - bawahanId: t.Optional(t.String()), - tipe: t.Optional(t.String()), - }), - }) - - // ❌ DELETE /del/:id - .delete("/del/:id", hubunganOrganisasiDelete); - -export default HubunganOrganisasi; diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/updt.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/updt.ts deleted file mode 100644 index 3d3491cd..00000000 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/hubungan-organisasi/updt.ts +++ /dev/null @@ -1,52 +0,0 @@ -import prisma from "@/lib/prisma"; -import { Context } from "elysia"; - -/* eslint-disable @typescript-eslint/no-explicit-any */ -type FormUpdateHubungan = { - atasanId?: string; - bawahanId?: string; - tipe?: string; -}; - -export default async function hubunganOrganisasiUpdate(context: Context) { - const body = await context.body as Omit; - const id = context.params?.id; - - if (!id) { - return { - success: false, - message: "ID wajib ada di URL", - }; - } - - try { - const updated = await prisma.hubunganOrganisasi.update({ - where: { id }, - data: { - atasanId: body.atasanId, - bawahanId: body.bawahanId, - tipe: body.tipe, - }, - }); - - return { - success: true, - message: "Hubungan organisasi berhasil diupdate", - data: updated, - }; - } catch (error: any) { - if (error.code === "P2002") { - return { - success: false, - message: "Relasi atasan-bawahan sudah ada", - }; - } - - console.error("Error update hubungan organisasi:", error); - return { - success: false, - message: "Gagal update data hubungan organisasi", - error: error.message, - }; - } -} diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/index.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/index.ts deleted file mode 100644 index b0ae7175..00000000 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import Elysia from "elysia"; -import PosisiOrganisasi from "./posisi-organisasi"; -import Pegawai from "./pegawai"; -import HubunganOrganisasi from "./hubungan-organisasi"; - -const StrukturOrganisasi = new Elysia({ - prefix: "/struktur-organisasi", - tags: ["Ekonomi/Struktur Organisasi"], -}) -.use(PosisiOrganisasi) -.use(Pegawai) -.use(HubunganOrganisasi) - -export default StrukturOrganisasi; \ No newline at end of file
Tidak ada data pegawai yang tersedia