Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

udpate integration #1

Merged
merged 14 commits into from
Dec 21, 2024
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ jobs:
run: npm install

# Step 4: Lint code
# - name: Lint code
# run: npm run lint
- name: Lint code
run: npm run lint
83 changes: 82 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,82 @@
# admin-Panel

# 🎛️ AirHopper Admin Panel

AirHopper Admin Panel adalah aplikasi web yang dirancang untuk membantu administrator dalam mengelola data dan fitur sistem pemesanan tiket pesawat AirHopper. Panel ini memberikan antarmuka yang intuitif untuk mengelola maskapai penerbangan, rute, pengguna, dan laporan transaksi.

## 🌟 Fitur Utama

- **Manajemen Maskapai Penerbangan**: Tambah, ubah, dan hapus data maskapai penerbangan dengan mudah.
- **Manajemen Penerbangan**: Kelola rute dan jadwal penerbangan.
- **Manajemen Pengguna**: Atur pengguna, termasuk otorisasi dan peran.
- **Laporan dan Statistik**: Pantau performa dan statistik melalui laporan interaktif.
- **Media Upload**: Dukungan untuk mengunggah gambar maskapai dengan integrasi ImageKit.
- **Autentikasi Aman**: Menggunakan JWT untuk akses dan manajemen keamanan.

## 🛠️ Teknologi yang Digunakan

- **Frontend Framework**: TailwindCSS untuk styling.
- **View Engine**: Edge.js untuk rendering halaman dinamis.
- **JavaScript**: Menggunakan Alpine.js untuk interaktivitas ringan.
- **Express.js**: Framework backend untuk mendukung API dan server-side rendering.
- **DataTable.js**: Membuat tabel data yang responsif dan interaktif.
- **Axios dan jQuery**: Untuk konsumsi API dan interaksi DOM.
- **Multer & ImageKit**: Mendukung upload file dan manajemen media.

## 🚀 Cara Menggunakan

### Prasyarat

1. Node.js (minimal versi 18.x).
2. Server backend API sudah berjalan (lihat [AirHopper Backend](https://github.com/AirHopper/BackEnd)).
3. Alat manajemen API (Postman atau browser).

### Langkah Instalasi

1. Clone repositori ini:
```bash
git clone https://github.com/AirHopper/AdminPanel.git
```
2. Masuk ke direktori proyek:
```bash
cd AdminPanel
```
3. Instal dependensi:
```bash
npm install
```
4. Konfigurasikan file lingkungan `.env`:
```env
API_URL="your-backend-api-url"
PORT=333
HOST="0.0.0.0"
NODE_ENV=development
```
5. Jalankan server:
```bash
npm start
```
6. Akses Admin Panel di:
```bash
http://localhost:3333
```
## 📄 Dokumentasi API

Admin Panel ini bergantung pada API backend untuk pengelolaan data. Pastikan API backend Anda sudah tersedia dan berjalan. Dokumentasi API tersedia di:

1. **Postman**: [Link Postman Collection](https://documenter.getpostman.com/view/33280373/2sAYBbf9a7)
2. **Swagger**: [Swagger API Docs](https://air-hopper-api.padek.tech/api-docs)

## 👥 Tim Pengembang

Admin Panel ini dikembangkan oleh tim AirHopper dari program Studi Independen Kampus Merdeka - Binar Academy.

- **Backend**:
- **Juan Verrel Tanuwijaya**
- **Muhamad Royhan Fadhli**
- **Ahmad Subhan Daryhadi**
- **Bima Rizqy Ramadhan**
- **Frontend dan Admin Panel**:
- **Ridhwan Tsalasah Putra**
- **Ryan Nicholas Purba**
- **M. Zaky Pria Maulana**
- **Joe Ferdinan**
29 changes: 29 additions & 0 deletions public/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,10 @@ video {
border-inline-start-width: 1px;
}

.border-t-2 {
border-top-width: 2px;
}

.border-none {
border-style: none;
}
Expand Down Expand Up @@ -1561,6 +1565,21 @@ video {
color: rgb(133 77 14 / var(--tw-text-opacity, 1));
}

.text-green-500 {
--tw-text-opacity: 1;
color: rgb(34 197 94 / var(--tw-text-opacity, 1));
}

.text-green-700 {
--tw-text-opacity: 1;
color: rgb(21 128 61 / var(--tw-text-opacity, 1));
}

.text-red-700 {
--tw-text-opacity: 1;
color: rgb(185 28 28 / var(--tw-text-opacity, 1));
}

.underline {
text-decoration-line: underline;
}
Expand Down Expand Up @@ -1744,6 +1763,11 @@ video {
background-color: rgb(250 204 21 / var(--tw-bg-opacity, 1));
}

.hover\:bg-red-400:hover {
--tw-bg-opacity: 1;
background-color: rgb(248 113 113 / var(--tw-bg-opacity, 1));
}

.hover\:bg-opacity-80:hover {
--tw-bg-opacity: 0.8;
}
Expand Down Expand Up @@ -1822,6 +1846,11 @@ video {
background-color: rgb(254 240 138 / var(--tw-bg-opacity, 1));
}

.focus\:bg-red-200:focus {
--tw-bg-opacity: 1;
background-color: rgb(254 202 202 / var(--tw-bg-opacity, 1));
}

.focus\:bg-opacity-80:focus {
--tw-bg-opacity: 0.8;
}
Expand Down
2 changes: 1 addition & 1 deletion src/modules/airline/airline.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const listAirplanes = async (req, res, next) => {
const { token } = req;

const airlinesResponse = await axios.get(`${api}/api/v1/airlines/${code}`);

const data = {
api: api,
token: token,
Expand Down
4 changes: 2 additions & 2 deletions src/modules/auth/auth.controller.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios from 'axios';

export const loginPage = async (req, res, next) => {
export const loginPage = async ( res, next) => {
try {
const api = process.env.API_URL;

Expand All @@ -17,7 +17,7 @@ export const loginPage = async (req, res, next) => {
}
};

export const login = async (req, res, next) => {
export const login = async (req, res) => {
try {
const api = process.env.API_URL;

Expand Down
2 changes: 1 addition & 1 deletion src/modules/tikcet/ticket.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const formatRupiah = (value) => {
export const index = async (req, res, next) => {
try {
const api = process.env.API_URL;
const token = req.cookies.token;
const {token} = req;

const tiketResponse = await axios.get(`${api}/api/v1/tickets?limit=999999`);
const airPortResponse = await axios.get(`${api}/api/v1/airports`);
Expand Down
3 changes: 3 additions & 0 deletions src/modules/user/user.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export const index = async (req, res, next) => {
},
});

const userData = usersResponse.data.data || [];
console.log({userData});

const data = {
token : token,
api : api,
Expand Down
12 changes: 6 additions & 6 deletions src/resources/views/components/ui/sidebar.edge
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,13 @@
Logout
@end
@slot('content')
<div class="flex flex-col gap-2">
<p>Apakah yakin mau keluar?</p>
<div class="flex flex-row gap-2 justify-between">
<button class="bg-red-500 text-white px-3 py-1 rounded-md hover:bg-red-600">Tidak</button>
<button type="button" onclick="logout()" class="bg-blue-500 text-white px-3 py-1 rounded-md hover:bg-blue-600" >Ya</button>
<div class="flex flex-col gap-y-2 text-center">
<h1 class="text-sm text-red-700">Are you sure you want to logout?</h1>
<div class="flex flex-row gap-x-2">
<button type="button" onclick="logout()" class="shadow-md py-2 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-red-300 text-red-800 hover:bg-red-400 focus:outline-none focus:bg-red-200 disabled:opacity-50 disabled:pointer-events-none text-center w-full justify-center">Yes</button>
<button type="button" @click="modalOpen=false" class="shadow-md py-2 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-blue-300 text-blue-800 hover:bg-blue-400 focus:outline-none focus:bg-blue-200 disabled:opacity-50 disabled:pointer-events-none text-center w-full justify-center">No</button>
</div>
</div>
</div>
@end
@end
</div>
Expand Down
75 changes: 71 additions & 4 deletions src/resources/views/pages/airline/index.edge
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,47 @@
<td><a href="/admin/airline/{{ airline.iataCode }}/airplanes" class="text-blue-600 hover:text-blue-800 hover:underline">{{ airline.name }}</a></td>
<td>{{ airline._count.Airplanes }}</td>
<td>
{{-- <a href="{{ api }}/airlines/{{ airline.id }}/edit" class="btn btn-primary">Edit</a> --}}
<button type="button" class="shadow-md py-1 px-3 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-yellow-300 text-yellow-800 hover:bg-yellow-400 focus:outline-none focus:bg-yellow-200 disabled:opacity-50 disabled:pointer-events-none">
Edit
</button>
<div class="flex flex-row gap-x-2">
@component('components/ui/modal', {
title: 'Edit Airline',
buttonClass: 'shadow-md py-2 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-yellow-300 text-yellow-800 hover:bg-yellow-400 focus:outline-none focus:bg-yellow-200 disabled:opacity-50 disabled:pointer-events-none',
})
@slot('header')
Edit
@end
@slot('content')
<div class="flex flex-col gap-y-2">
<h1 class="text-sm text-gray-500">Edit Airline</h1>
{{ airline.iataCode }}

<div class="flex flex-row gap-x-2">
{{-- <button type="button" onclick="editAirline({{ airline.iataCode }})" class="shadow-md py-2 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-red-300 text-red-800 hover:bg-red-400 focus:outline-none focus:bg-red-200 disabled:opacity-50 disabled:pointer-events-none text-center w-full justify-center">Delete</button> --}}
<button type="button" @click="modalOpen=false" class="shadow-md py-2 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-blue-300 text-blue-800 hover:bg-blue-400 focus:outline-none focus:bg-blue-200 disabled:opacity-50 disabled:pointer-events-none text-center w-full justify-center">Cancel</button>
</div>
</div>

@end
@end
@component('components/ui/modal', {
title: 'Delete Airline',
buttonClass: 'shadow-md py-2 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-red-300 text-red-800 hover:bg-red-400 focus:outline-none focus:bg-red-200 disabled:opacity-50 disabled:pointer-events-none',
})
@slot('header')
Delete
@end
@slot('content')
<div class="flex flex-col gap-y-2">
<h1 class="text-sm text-gray-500">Are you sure you want to delete this Airline?</h1>

<div class="flex flex-row gap-x-2">
<button type="button" onclick="deleteAirline('{{ airline.iataCode }}')" class="shadow-md py-2 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-red-300 text-red-800 hover:bg-red-400 focus:outline-none focus:bg-red-200 disabled:opacity-50 disabled:pointer-events-none text-center w-full justify-center">Delete</button>
<button type="button" @click="modalOpen=false" class="shadow-md py-2 px-4 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-blue-300 text-blue-800 hover:bg-blue-400 focus:outline-none focus:bg-blue-200 disabled:opacity-50 disabled:pointer-events-none text-center w-full justify-center">Cancel</button>
</div>
</div>

@end
@end
</div>
</td>
</tr>
@end
Expand Down Expand Up @@ -116,8 +153,38 @@
}
});
});


});

function deleteAirline(iataCode) {
$.ajax({
url: '{{ api }}/api/v1/airlines/' + iataCode,
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`
},
success: function (response) {
showToast("success", "Success", "Airline deleted successfully!");
setTimeout(() => {
window.location.reload();
}, 1000);
},
error: function (xhr) {
if (xhr.responseJSON && xhr.responseJSON.errors) {
const errors = xhr.responseJSON.errors;
let errorMessages = '';
errors.forEach(error => {
errorMessages += `${error.field}: ${error.message}\n`;
});
showToast("error", "Error", errorMessages);
} else {
showToast("error", "Error", "An error occurred. Please try again.");
}
},
});
}

</script>

@endslot
Expand Down
Loading
Loading