Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a61c5d0
Menambahkan fitur usulan pengadaan
divaramadhani18 Jun 6, 2026
c1ad082
first commit
END0310 Jun 8, 2026
cc7deac
Delete PENGADAAN_SETUP.md setup documentation
END0310 Jun 8, 2026
6bdb07d
Delete facultyware-main/PENGADAAN_SETUP.md
END0310 Jun 8, 2026
db73a30
Merge branch 'husnilk:main' into pengelola-aset
END0310 Jun 8, 2026
ae3d7e7
Merge pull request #1 from END0310/pengelola-aset
END0310 Jun 8, 2026
b2e8120
Delete facultyware-main directory
END0310 Jun 8, 2026
0d77348
fiturr pengelola asset
END0310 Jun 8, 2026
764513d
Resolve merge conflicts - use local versions
END0310 Jun 8, 2026
ac7534f
hapus file env
END0310 Jun 8, 2026
8adb650
Update fitur usulan pengadaan
divaramadhani18 Jun 9, 2026
da4f2f0
Menyelesaikan fitur persetujuan dan pengelolaan pengadaan barang Waki…
aldoseptia Jun 9, 2026
62d9aca
Refactor admin and pengelola aset modules
END0310 Jun 10, 2026
013b0c9
Merge branch 'persetujuan-pengadaan' into integrasi-main
END0310 Jun 10, 2026
01c9f45
Merge remote-tracking branch 'origin/usulan-pengadaan' into integrasi…
END0310 Jun 10, 2026
cbe54f9
Fix wakildekan integration routing
END0310 Jun 10, 2026
a4630fb
Initial commit: facultyware integrasi project
END0310 Jun 10, 2026
3364418
Merge branch 'integrasi-main' of https://github.com/END0310/facultywa…
END0310 Jun 10, 2026
f60f826
Fix pengelola aset dashboard and approval flow
END0310 Jun 11, 2026
8c82c33
Update fitur usulan pengadaan
divaramadhani18 Jun 12, 2026
38e06f1
Resolve merge conflict: merge usulan-pengadaan ke integrasi-main
aldoseptia Jun 13, 2026
135bd8a
Menambahkan halaman dashboard dengan statistik dan tabel ringkasan
aldoseptia Jun 13, 2026
4f5bef8
Tambah fitur pencarian (searching) dan pagination di semua halaman da…
aldoseptia Jun 14, 2026
69a6195
Perbaiki error handling di modul Wakil Dekan agar informatif di sisi …
aldoseptia Jun 14, 2026
82a0b64
Hapus dead route /users yang tidak memiliki kontrol akses
aldoseptia Jun 14, 2026
50b25fd
integrasi fitur dan perbaikan route pada ketuadepartemen
END0310 Jun 15, 2026
1409327
perbaikan dashboard dan sidebar pada ketua departemen
END0310 Jun 15, 2026
f7ac1e4
facultyware integrasi project
divaramadhani18 Jun 15, 2026
554719a
Menambahkan End-to-End test suite menggunakan Playwright untuk seluru…
aldoseptia Jun 16, 2026
72f99c1
Menambahkan End-to-End test suite menggunakan playwright untuk seluru…
divaramadhani18 Jun 16, 2026
bee3ee9
Merge pull request #3 from END0310/integrasi-main
END0310 Jun 22, 2026
fd0cd9d
Update fitur usulan pengadaan
divaramadhani18 Jun 22, 2026
672a781
Hapus fitur admin dan tambah README
END0310 Jun 22, 2026
da8cb41
perbaiki detail, modal konfirmasi, dan laporan PDF wakildekan
aldoseptia Jun 22, 2026
66459d1
perbaiki playwright test agar semua 33 test passed
aldoseptia Jun 23, 2026
5d68c96
Sesuaikan env database dan seed procurement
END0310 Jun 23, 2026
569be96
Ubah sapaan halaman home
END0310 Jun 23, 2026
02725eb
Update fitur usulan pengadaan
divaramadhani18 Jun 24, 2026
d9a68ef
Mengupdate fitur Persetujuan Pengadaan
aldoseptia Jun 24, 2026
bdbdc56
Perbaiki fitur generate PDF modul Wakil Dekan
aldoseptia Jun 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,10 @@ typings/

# next.js build output
.next

# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/.auth/
2 changes: 1 addition & 1 deletion GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ To protect a route with a specific permission (or any one of multiple permission
const { checkPermission } = require('../middlewares/acl');

// Single permission
router.get('/admin/users', checkPermission('manage_users'), userController.list);
router.get('/reports', checkPermission('view_reports'), reportController.index);

// Multiple permissions (user must have at least one)
router.get('/reports', checkPermission(['view_reports', 'manage_all']), reportController.index);
Expand Down
93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Facultyware

Facultyware adalah aplikasi web sistem informasi pengadaan barang untuk lingkungan fakultas. Aplikasi ini membantu proses pengajuan usulan pengadaan barang oleh Ketua Departemen, pengelolaan permohonan oleh Pengelola Aset/Pengelola Sistem, serta proses persetujuan atau penolakan oleh Wakil Dekan.

Fitur utama aplikasi meliputi pengajuan usulan pengadaan, edit dan hapus usulan, pemantauan status usulan, pengelolaan permohonan pengadaan, penambahan barang hasil pengadaan ke sistem, dashboard tiap role, laporan rekapan, dan endpoint API JSON.

## Cara Instalasi

1. Pastikan Node.js dan MySQL sudah terpasang.

2. Masuk ke folder project:

```bash
cd facultyware
```

3. Install dependency:

```bash
npm install
```

4. Buat atau sesuaikan file `.env`:

```env
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=
DB_NAME=facultyware
SESSION_SECRET=facultyware-secret
PORT=3000
```

5. Buat database MySQL dengan nama sesuai `DB_NAME`.

6. Jalankan script inisialisasi atau seed database jika diperlukan:

```bash
node scripts/init_db.js
node scripts/seed_data.js
```

Jika menggunakan seed SQL pengadaan, jalankan file:

```text
database/seed_procurement_roles.sql
```

## Cara Menjalankan Aplikasi

Jalankan aplikasi dengan perintah:

```bash
npm start
```

Setelah server berjalan, buka aplikasi melalui browser:

```text
http://localhost:3000
```

Untuk mode development, gunakan:

```bash
npm run dev
```

## Pembagian Tugas Anggota

| No | Fitur | NIM | Nama Anggota |
| --- | --- | --- | --- |
| 1 | Ketua Departemen dapat menginputkan pengajuan usulan pengadaan barang | 2411521017 | Diva Ramadhani |
| 2 | Ketua Departemen dapat mengedit usulan pengadaan barang | 2411521017 | Diva Ramadhani |
| 3 | Ketua Departemen dapat melihat status usulan pengadaan barang | 2411521017 | Diva Ramadhani |
| 4 | Ketua Departemen dapat menghapus usulan pengadaan barang | 2411521017 | Diva Ramadhani |
| 5 | Ketua Departemen dapat mengenerate file laporan rekapan pengadaan barang | 2411521017 | Diva Ramadhani |
| 6 | Sistem dapat memberikan response semua riwayat pengajuan pengadaan barang oleh Ketua Departemen dalam format JSON (API) | 2411521017 | Diva Ramadhani |
| 7 | Ketua Departemen dapat melihat dashboard | 2411521017 | Diva Ramadhani |
| 8 | Pengelola sistem dapat menerima usulan pengadaan barang | 2411522006 | Kevin Rahmat Illahi |
| 9 | Pengelola Aset dapat menginputkan permohonan pengadaan barang | 2411522006 | Kevin Rahmat Illahi |
| 10 | Pengelola Aset dapat menambahkan data barang yang disetujui hasil dari pengadaan ke sistem | 2411522006 | Kevin Rahmat Illahi |
| 11 | Pengelola Aset dapat mengenerate file laporan rekapan pengadaan barang | 2411522006 | Kevin Rahmat Illahi |
| 12 | Pengelola Aset dapat merubah status usulan pengadaan barang | 2411522006 | Kevin Rahmat Illahi |
| 13 | Pengelola Aset dapat melihat data barang pengadaan melalui API | 2411522006 | Kevin Rahmat Illahi |
| 14 | Pengelola Aset dapat melihat dashboard | 2411522006 | Kevin Rahmat Illahi |
| 15 | Wakil Dekan dapat melihat daftar permohonan pengadaan barang | 2411522013 | Aldo Septia Elvawan |
| 16 | Wakil Dekan dapat melihat detail permohonan pengadaan barang | 2411522013 | Aldo Septia Elvawan |
| 17 | Wakil Dekan dapat memberikan keputusan persetujuan/penolakan permohonan pengadaan barang | 2411522013 | Aldo Septia Elvawan |
| 18 | Wakil Dekan dapat melihat riwayat keputusan permohonan pengadaan barang | 2411522013 | Aldo Septia Elvawan |
| 19 | Wakil Dekan dapat mengenerate file laporan rekapan pengadaan barang | 2411522013 | Aldo Septia Elvawan |
| 20 | Sistem dapat memberikan response data permohonan pengadaan barang dalam format JSON (API) | 2411522013 | Aldo Septia Elvawan |
| 21 | Wakil Dekan dapat melihat dashboard | 2411522013 | Aldo Septia Elvawan |
66 changes: 44 additions & 22 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
require('dotenv').config();
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env') });
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var session = require('express-session');
var MySQLStore = require('express-mysql-session')(session);

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var authRouter = require('./routes/auth/index');
var pengelolaAsetDashboardRouter = require('./routes/pengelola-aset/dashboard');
var procurementsRouter = require('./routes/pengelola-aset/procurements');
var apiProcurementsRouter = require('./routes/pengelola-aset/apiProcurements');
const { notFoundHandler, errorHandler } = require('./middlewares/error');
const usulanRouter = require('./routes/usulan');
const wakildekanRouter = require('./routes/wakildekan');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

Expand All @@ -22,32 +25,51 @@ app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

// Session configuration
const sessionStore = new MySQLStore({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
});
// Default pakai MemoryStore supaya tidak bentrok dengan tabel sessions Laravel
// yang memiliki kolom payload/last_activity, bukan data/expires.
let sessionStore;
if (process.env.USE_MYSQL_SESSION === 'true') {
sessionStore = new MySQLStore({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
createDatabaseTable: false,
schema: {
tableName: process.env.EXPRESS_SESSION_TABLE || 'express_sessions',
columnNames: {
session_id: 'session_id',
expires: 'expires',
data: 'data'
}
}
});
}

app.use(session({
key: 'session_cookie_name',
secret: process.env.SESSION_SECRET || 'secret',
key: 'facultyware_session',
secret: process.env.SESSION_SECRET || 'facultyware-secret',
store: sessionStore,
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 1000 * 60 * 60 * 24 // 1 day
}
cookie: { maxAge: 1000 * 60 * 60 * 24 }
}));

app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use((req, res, next) => {
res.locals.currentUser = req.session.user || null;
res.locals.flash = req.session.flash || null;
delete req.session.flash;
next();
});

app.use('/', authRouter);
app.use('/', pengelolaAsetDashboardRouter);
app.use('/usulan', usulanRouter);
app.use('/wakildekan', wakildekanRouter);
app.use('/procurements', procurementsRouter);
app.use('/api', apiProcurementsRouter);

// catch 404 and forward to error handler
app.use(notFoundHandler);

// error handler
app.use(errorHandler);

module.exports = app;
76 changes: 76 additions & 0 deletions controllers/auth/authController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const bcrypt = require('bcryptjs');
const db = require('../../lib/db');

const index = (req, res) => res.redirect('/home');

const renderLogin = (res, overrides = {}) => {
res.render('auth/login', {
title: 'Login',
error: null,
success: null,
form: { email: '' },
...overrides
});
};

const loginPage = (req, res) => {
if (req.session.userId) return res.redirect('/home');
renderLogin(res);
};

const login = async (req, res, next) => {
const email = String(req.body.email || req.body.username || '').trim();
const password = String(req.body.password || '');

try {
const [rows] = await db.query('SELECT * FROM users WHERE email = ? OR name = ? LIMIT 1', [email, email]);
if (!rows.length) {
return renderLogin(res, { error: 'Email atau password tidak valid.', form: { email } });
}

const user = rows[0];
const isBcrypt = String(user.password || '').startsWith('$2');
const isMatch = isBcrypt ? await bcrypt.compare(password, user.password) : password === user.password;
if (!isMatch) {
return renderLogin(res, { error: 'Email atau password tidak valid.', form: { email } });
}

const [employeeRows] = await db.query('SELECT id, name FROM employees WHERE id = ? LIMIT 1', [user.id]);
const [roleRows] = await db.query(`
SELECT r.name
FROM roles r
JOIN model_has_roles mhr ON mhr.role_id = r.id
WHERE mhr.model_id = ?
`, [user.id]);

req.session.userId = user.id;
req.session.username = user.name;
req.session.email = user.email;
req.session.employeeId = employeeRows[0]?.id || user.id;
req.session.roles = roleRows.map(row => row.name);
req.session.user = {
id: user.id,
name: user.name,
email: user.email,
employeeId: req.session.employeeId,
roles: req.session.roles
};

if (req.session.roles.includes('Wakil Dekan') || req.session.roles.includes('wakildekan')) {
return res.redirect('/wakildekan/permohonan');
}

res.redirect('/home');
} catch (err) {
next(err);
}
};

const logout = (req, res, next) => {
req.session.destroy((err) => {
if (err) return next(err);
res.redirect('/login');
});
};

module.exports = { index, loginPage, login, logout };
Loading