Commit 087fe5e9 authored by Faris Rizki Ekananda's avatar Faris Rizki Ekananda 💬
Browse files

Pindah Github

parent 38262b7c
Pipeline #28528 canceled with stages
config/db_keys.config.php
test.php
temp.php
test.html
\ No newline at end of file
RewriteEngine on
RewriteCond ${REQUEST_FILENAME} !-d
RewriteCond ${REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?$1 [QSA,L]
\ No newline at end of file
# Tugas 1 IF3110 Pengembangan Aplikasi Berbasis Web
# Willy Wangky Chocolate Online Shop
## Tujuan Pembelajaran Tugas Besar 1
---
* Mahasiswa mampu membuat sebuah aplikasi web dengan menggunakan HTML, CSS dan JS.
* Mahasiswa mampu membuat sebuah layanan web dengan menggunakan PHP.
* Mahasiswa mampu menggunakan AJAX
* Mahasiswa memahami penangananan state dan perbedaan HTTP method.
Website Willy Wangky adalah website berbasis php yang digunakan untuk membeli coklat dari pabrik coklat terbesar dan terenak di dunia, Willy Wangky
## Deskripsi Singkat
## Dependency
<div align="center">
<img src="https://i.imgur.com/0NI6Mkf.png" alt=""/>
</div>
---
1. [XAMPP](https://www.apachefriends.org/index.html)
2. inisialisasi database (menggunakan `init_db.php` dan `init_dummy_data.php`)
3. Sejauh ini, browser yang support aplikasi kami hanyalah **Mozilla Firefox**, karena ada masalah dengan policy setcookie sehingga kami tidak dapat menambahkan cookie dengan set cookie
Anda mungkin sudah tahu mengenai pabrik coklat terbesar seantero dunia, Willy Wangky.
Akan tetapi, produsen terbaik tidak akan sukses tanpa konsumen dan distributor terbaik.
Sebab coklat dari Willy Wangky sangat disenangi konsumen, maka Willy Wangky membutuhkan distributor yang handal dalam menangani penjualan coklat.
Untungnya, Willy Wangky mengenal Jan.
Jan sudah sangat pengalaman dengan distribusi makanan dan minuman ringan.
Bahkan, Jan sudah memiliki usaha sendiri bernama Jan’s Cook.
## How to Install
Willy Wangky pun meminta Jan untuk memberikan saran bagaimana cara menjual coklat-coklat miliknya pada konsumen.
Apalagi di tengah pandemi seperti ini, beberapa toko penjualan sepi dikunjungi pengunjung.
Jan tanpa pikir panjang, memberikan saran mengenai penjualan daring menggunakan aplikasi berbasis web.
Willy Wangky sangat senang dengan hal ini, dan segera mengutus Jan untuk mencari programmer terbaik untuk pengembangannya.
---
Willy Wangky menginginkan web tersebut agar penggunanya dapat melakukan pendaftaran akun, login, logout, pencarian produk, mendapatkan penjelasan produk secara detail, pembelian produk dan dapat melihat riwayat pembelian produk, dan pekerjanya dapat dengan mudah menambahkan jenis coklat baru yang ingin dijual serta menambah ketersediaan coklat.
1. install [XAMPP](https://www.apachefriends.org/index.html)
2. masukkan path directory `php.exe` pada instalasi XAMPP `...xampp\php` (biasanya terdapat di `C:\xampp\php`) ke dalam PATH environment variables.
Jan telah membuat desain user interface dengan low fidelity.
Sekarang, dia merekrut kalian untuk membuat sebuah aplikasi web yang membantu penjualan coklat milik Willy Wangky.
Disebabkan Jan sangat percaya dengan kalian, maka web yang kalian kembangkan dapat kalian hias dengan sebaik mungkin.
Perlu diingat bahwa tata letak komponen harus mengikuti desain dari Jan.
## How to Run
## Tools
---
* Untuk frontend, gunakan Javascript, HTML dan CSS. Tidak boleh menggunakan library atau framework CSS atau JS (e.g. JQuery, lodash, atau Bootstrap). CSS sebisa mungkin ada di file yang berbeda dengan HTML (tidak inline styling).
* Untuk backend, wajib menggunakan PHP tanpa framework apapun. Harap diperhatikan, Anda harus mengimplementasikan fitur menggunakan HTTP method yang tepat.
* Gunakan MySQL untuk menyimpan data.
1. Jalankan XAMPP
2. Start Apache dan MySQL Module pada XAMPP
3. Di direktori tempat repository berada, jalankan:
```
php -S localhost:8000
```
Sebagai contoh digunakan port 8000.
## Spesifikasi
4. Jalankan browser dan pergi ke link `localhost:8000` .
Sebagai contoh jika ingin pergi ke dashboard, pada project kami dapat dilakukan navigasi ke `localhost:8000/src/dasboard.php`
### Login Page
![](mockup/Login.png)
Halaman pertama yang ditampilkan jika pengguna belum login atau sudah logout adalah halaman Login.
Pengguna dapat melakukan login sebagai user atau superuser. Login hanya membandingkan email dan password saja.
Tidak perlu tambahan proteksi apapun.
Identitas pengguna yang sudah login akan disimpan sebagai cookie dalam browser. Cookie menyimpan data pengguna dalam bentuk string dengan panjang tertentu. Untuk mengetahui pengguna mana yang sedang login, string tersebut dapat dilihat di basis data. Identitas tersebut tidak boleh disimpan sebagai parameter HTTP GET. Jika cookie ini tidak ada, maka pengguna dianggap belum login dan aplikasi akan selalu mengarahkan (redirect) pengguna ke halaman ini, meskipun pengguna membuka halaman yang lain. Masa berlaku cookie dibebaskan.
### Register Page
![](mockup/Register.png)
Pengguna dapat mendaftarkan akun baru jika belum login atau sudah logout.
Pada halaman ini, pengguna mendaftarkan diri dengan email dan username yang unik.
Pengguna tidak dapat mendaftar sebagai superuser, karena superuser ditambahkan secara manual pada basis data.
Pengecekan keunikan nilai field dilakukan menggunakan AJAX. Jika unik, border field akan berwarna hijau.
Jika tidak unik, akan muncul pesan error pada form.
Validasi lain yang dilakukan pada sisi klien pada halaman ini adalah:
* Email memiliki format email standar seperti “example@example.com”.
* Username hanya menerima kombinasi alphabet, angka, dan underscore.
Setelah semua nilai field sudah diisi dan valid, pengguna dapat mendaftarkan akun barunya.
Jika akun berhasil didaftarkan, pengguna langsung diarahkan ke halaman Dashboard.
Mekanisme cookie sama dengan halaman Login.
### Dashboard page
![](mockup/Dashboard.png)
Pada halaman Dashboard, pengguna disambut dengan username pengguna dan daftar coklat yang tersedia.
Coklat ditampilkan secara terurut sesuai dengan banyak coklat yang terjual, mulai dari yang paling banyak sampai yang paling sedikit.
Banyak coklat yang ditampilkan dibatasi hanya sampai 10 coklat dengan penjualan terbanyak.
Pengguna dapat melihat detail coklat dengan mengklik gambar atau nama coklat.
Header aplikasi web untuk user terdiri dari search bar, pilihan untuk melihat daftar transaksi, dan pilihan untuk logout, sedangkan untuk superuser terdiri dari search bar, pilihan untuk menambah jenis coklat, dan pilihan untuk logout. Search bar digunakan untuk mencari coklat berdasarkan nama. Hasil pencarian ditampilkan pada halaman Search Result. Jika pengguna memilih untuk logout, pengguna akan diarahkan ke halaman Login.
### Search Result page
![](mockup/Search.png)
Hasil pencarian dari search bar di halaman Dashboard akan ditampilkan pada halaman ini. Untuk setiap coklat, ditampilkan informasi nama, deskripsi, banyak coklat terjual, dan gambar coklat. Pengguna dapat melihat detail coklat dengan menekan bagian manapun pada section coklat tersebut.
Jika daftar coklat melebihi jumlah tertentu (jumlah didefinisikan sendiri), maka akan muncul pagination untuk melihat daftar coklat selebihnya. Ketika memilih page, pengguna tidak diarahkan ke halaman baru namun daftar coklat langsung berubah di halaman ini.
### Chocolate Detail page
![](mockup/Detail%20User.png)
Pada halaman Chocolate Detail, terdapat beberapa informasi mengenai coklat yang dipilih,
yaitu nama, gambar, banyak coklat terjual, deskripsi, harga, dan ketersediaan dari coklat tersebut.
Jika coklat dengan jenis tersebut masih tersedia, pengguna dapat memilih tombol “Buy Now” yang kemudian akan menampilkan banyak coklat yang dibeli,
alamat pengiriman, total harga, tombol “Cancel” dan tombol “Buy” sebagai berikut:
![](mockup/Buy.png)
Pengguna memilih jumlah pembelian coklat dan alamat pengiriman.
Perubahan total harga ditampilkan secara real-time sesuai dengan perubahan jumlah pembelian coklat.
Pengguna tidak dapat melakukan pembelian coklat melebihi banyak coklat yang tersedia.
(Asumsi saat proses pembelian coklat, ketersediaan coklat tidak berubah).
Pastikan setelah proses pembelian, ketersediaan coklat berubah sebanyak jumlah yang dibeli.
Jika pengguna login sebagai superuser, tombol “Buy Now” digantikan oleh tombol “Add Stock” yang jika ditekan akan menampilkan banyak coklat yang ingin ditambah, tombol “Cancel” dan tombol “Add” sebagai berikut :
![](mockup/Detail%20Admin.png)
![](mockup/Add%20Stock.png)
### Transaction History Page
![](mockup/History.png)
Pada halaman ini, ditampilkan daftar coklat yang telah dibeli.
Daftar diurutkan berdasarkan tanggal pembelian coklat.
Untuk setiap transaksi, ditampilkan informasi nama, jumlah pembelian, total harga, waktu pembelian dan alamat pengiriman.
Pengguna dapat mengetahui detail coklat yang telah dibeli dengan menekan nama coklat yang akan mengarahkan pengguna ke halaman Chocolate Detail.
### Add New Chocolate Page
![](mockup/Add.png)
Halaman ini hanya bisa diakses oleh superuser melalui pilihan untuk menambah jenis coklat pada header web.
Pada halaman ini, superuser dapat menambah jenis coklat yang ada beserta detail dari coklat tersebut.
Detail dari coklat meliputi nama, gambar, deskripsi, harga, dan ketersediaan dari coklat tersebut.
Seluruh detail pada coklat harus diisi sebagai persyaratan coklat dapat ditambahkan.
## Keterangan Tambahan
* Jam pada aplikasi web mengacu pada jam lokal pengguna.
* Basis data didefinisikan sendiri.
## Bonus
Catatan: Kerjakan dahulu spesifikasi wajib sebelum mengerjakan bonus.
1. Data yang disimpan pada cookie memiliki expiry time. Jika access token ini tidak ada atau tidak valid, maka pengguna dianggap belum login. Expiry time sebuah access token berbeda dengan waktu berlaku cookie.
2. Pada halaman Buy Chocolate, ketersediaan coklat diperbaharui secara real-time. Contoh jika pengguna A ingin membeli persediaan terakhir coklat dengan jenis tersebut dan ternyata pengguna B telah membeli coklat terakhir tersebut, maka ketersediaan coklat perlu diperbaharui. Akibatnya, pengguna A tidak dapat membeli coklat dengan jenis tersebut. Pembaharuan ketersediaan coklat menggunakan AJAX.
3. Tampilan dibuat responsif (minimal untuk ukuran 1280x768 dan 800x600). Artinya, tampilan mungkin berubah menyesuaikan ukuran layar.
## Bantuan
Untuk membantu anda dalam mengerjakan tugas ini, ada beberapa keyword yang bisa anda cari untuk menyelesaikan tugas ini.
| | Kata Kunci |
| ------------ | ----------------------------------------------------------------------------------------------------- |
| HTTP methods | get, post, put, delete |
| CSS | margin, padding, font-size, text-align, flex, grid, border, color, div, span, anchor tag, box-shadow |
| Javascript | XMLHTTPRequest, addEventListener |
| PHP | mysqli_connect, mysql_query, $_GET, $_POST, $_COOKIE, var_dump, print_r, echo, require, fungsi header |
| SQL query | SELECT, INSERT, UPDATE, DELETE, WHERE, operator LIKE |
Jika ada pertanyaan, silahkan bertanya di channel Tubes 1 di MS Teams.
## Anggota Kelompok
Anggota kelompok dapat diisi pada spreadsheet berikut : https://docs.google.com/spreadsheets/d/1l2H6Er4lZjyiO2ehqlBKOOZT5gE1pLukw2EHTGzqY3U/edit?usp=sharing
## Petunjuk Pengerjaan
1. Buatlah grup pada Gitlab dengan format "IF3110-2020-01-KXX-YY", dengan XX adalah nomor kelas dan YY adalah nomor kelompok.
2. Tambahkan anggota kelompok pada grup anda.
3. Fork pada repository ini dengan grup yang telah dibuat.
4. Ubah hak akses repository hasil Fork anda menjadi private.
5. Silakan commit pada repository anda (hasil fork). Lakukan beberapa commit dengan pesan yang bermakna, contoh: `add login form`, `fix login bug`, jangan seperti `final`, `dikit lagi`, `benerin bug`.
6. Buatlah file README yang berisi:
* Deskripsi aplikasi web
* Daftar requirement
* Cara instalasi
* Cara menjalankan server
* Screenshot tampilan aplikasi, dan
* Penjelasan mengenai pembagian tugas masing-masing anggota (lihat formatnya pada bagian pembagian tugas).
## Pengumpulan Tugas
Deadline tugas adalah pada hari Minggu, 25 Oktober 2020 pukul 19.00 WIB. Waktu pengumpulan tugas yang dilihat adalah waktu push ke server Gitlab terakhir.
## Screenshot dari Tugas
-------
1. Login ![login](./docs/img/login.PNG)
2. Register ![register](./docs/img/register.PNG)
3. Dashboard ![dashboard](./docs/img/dashboard.PNG)
4. New Chocolate ![new-choc](./docs/img/new_choco.PNG)
5. Search Result ![search](./docs/img/search.PNG)
6. Transaction History ![history](./docs/img/history.PNG)
7. Detail ![history](./docs/img/detail.PNG)
8. Buy Chocolate ![history](./docs/img/detail.PNG)
9. Add Stock ![history](./docs/img/detail.PNG)
## Pembagian Tugas
Setiap anggota kelompok diwajibkan untuk mengerjakan bagian frontend dan backend.
---
### Frontend
1. Login : 13518xxx, 13518xxx
2. Register : 13518xxx
3. (Lanjutkan …)
### Backend
1. Login : 13518xxx, 13518xxx
2. Register : 13518xxx
3. (Lanjutkan…)
1. Login: 13518125
2. Register: 13518146, 13518125
3. Dashboard: 13518135
4. Chocolate Detail: 13518125, 13518135
5. Buy Chocolate: 13518135
6. Add Chocolate: 13518135
7. Search Results: 13518125
8. Transaction History: 13518135
9. New Chocolate: 13518146, 13518125
## About
Asisten IF3110 - 2020
### Backend
Abel | Agwar | Asif | Iwang | Meyer | Vendra
\ No newline at end of file
1. Login: 1351846
2. Register: 13518146
3. Dashboard: 13518135
4. Chocolate Detail: 13518125
5. Buy Chocolate: 13518135
6. Add Chocolate: 13518135
7. Search Results: 13518125
8. Transaction History: 13518135, 13518146
9. New Chocolate: 13518146
10. DB Model: 13518146
### Bonus
1. Expire time session at DB: 13518146
2. AJAX di real-time stock: 13518146,
3. Responsive: ....
<?php
define('SECRET_KEY', 'uhuhuhu');
?>
\ No newline at end of file
<?php
define('CHOCO_IMG_DIR', 'resources/chocolate_img/');
?>
\ No newline at end of file
<?php
require_once('db_keys.config.php');
// Using mysqli
$conn = new mysqli(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
if($conn->connect_error){
die("conn failed: " . $conn->connect_error);
}
$sql = " DROP TABLE IF EXISTS users, chocolates, transactions, sessions";
if ($conn->query($sql) === TRUE){
echo "existing table(s) already deleted\n";
} else {
echo "ERROR when try to delete existing table(s)\n";
}
$sql = "CREATE TABLE users (
username VARCHAR(50) PRIMARY KEY,
email VARCHAR(100) UNIQUE NOT NULL,
password_hash VARCHAR(100) NOT NULL,
is_superuser BOOLEAN NOT NULL
)";
if ($conn->query($sql) === TRUE){
echo "users table successfully created\n";
} else {
echo "ERROR create table users\n";
}
$sql = "CREATE TABLE chocolates (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
amount INT(10) UNSIGNED NOT NULL,
price INT(12) UNSIGNED NOT NULL,
description TEXT,
image_extension VARCHAR(10)
)";
if ($conn->query($sql) === TRUE){
echo "chocolates table successfully created\n";
} else {
echo "ERROR create table chocolates\n";
}
$sql = "CREATE TABLE transactions (
-- id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
chocolate_id INT(6) UNSIGNED NOT NULL,
amount INT(10) UNSIGNED NOT NULL,
totalprice INT(18) UNSIGNED NOT NULL,
address TEXT NOT NULL,
time DATETIME NOT NULL,
PRIMARY KEY (username, chocolate_id, time),
FOREIGN KEY (chocolate_id) REFERENCES chocolates(id),
FOREIGN KEY (username) REFERENCES users(username)
)";
// salat satu cara buat timestamp :
// reg_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
if ($conn->query($sql) === TRUE){
echo "transactions table successfully created\n";
} else {
echo "ERROR create table transactions\n";
}
$sql = "CREATE TABLE sessions (
hash_id VARCHAR(65) NOT NULL,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
is_superuser BOOLEAN NOT NULL,
login_time DATETIME NOT NULL,
expire_time DATETIME NOT NULL,
PRIMARY KEY (hash_id),
FOREIGN KEY (email) REFERENCES users(email),
FOREIGN KEY (username) REFERENCES users(username)
)";
// salah satu cara buat timestamp :
// reg_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
if ($conn->query($sql) === TRUE){
echo "sessions table successfully created\n";
} else {
echo "ERROR create table sessions\n";
}
$sql = "SET GLOBAL event_scheduler = ON";
if ($conn->query($sql) === TRUE){
echo "set event scheduling options successfully\n";
} else {
echo "ERROR set event scheduling options\n";
}
$sql = "CREATE EVENT cleanup
ON SCHEDULE EVERY 5 MINUTE
DO DELETE FROM sessions
WHERE expire_time < CURRENT_TIMESTAMP()";
if ($conn->query($sql) === TRUE){
echo "cleanup sessions event successfully created\n";
} else {
echo "ERROR create cleanup sessions event\n";
}
$conn->close();
?>
\ No newline at end of file
<?php
require_once('db_keys.config.php');
// Using mysqli
$conn = new mysqli(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
if($conn->connect_error){
die("conn failed: " . $conn->connect_error);
}
$dummies = [
"INSERT INTO `chocolates` VALUES (1,'Dairy Milk',5,12000,'ntap','.jpg');",
"INSERT INTO `chocolates` VALUES (2,'Silver Queen',2,15000,'enak bingits','.jpg');",
"INSERT INTO `chocolates` VALUES (3,'Kinder Joy',10,10000,'enak pisan euy','.jpg');",
"INSERT INTO `chocolates` VALUES (4,'Toblerone',32,18000,'rasanya anjim banget','.jpg');",
"INSERT INTO `chocolates` VALUES (5,'Kitkat Green',1,22000,'booming banget dulu sampe PO ke jepun','.jpg');",
"INSERT INTO `chocolates` VALUES (6,'Kitkat White',7,19000,'putih bersih merona','.jpg');",
"INSERT INTO `chocolates` VALUES (7,'Kitkat Dark',9,21000,'hitam pekat kayak tubes','.jpg');",
"INSERT INTO `chocolates` VALUES (8,'Kitkat Hello Kitty',0,25000,'gue bahkan belom pernah nyoba. Emang ada yak?','.jpg');",
"INSERT INTO `chocolates` VALUES (9,'Dove',11,8000,'Di Borma murah wgwg','.jpg');",
"INSERT INTO `chocolates` VALUES (10,'Alpine',12,9000,'Ini agak mahal dikit dari dove tapi mayan lah','.jpg');",
"INSERT INTO `chocolates` VALUES (11,'Aice',2,3000,'MURAH BANGET ENAK LAGI tapi rasa keringat dan darah','.jpg');",
"INSERT INTO `chocolates` VALUES (12,'Chocolatos',5,1000,'Udah kayak tahu bulat harganya','.jpg');",
"INSERT INTO `users` VALUES ('admin', 'a@g.com', '" . password_hash('123', PASSWORD_BCRYPT) . "', 1);",
"INSERT INTO `users` VALUES ('spongebob', 'spongebob@gmail.com', '" . password_hash('kuning', PASSWORD_BCRYPT) ."', 0);",
"INSERT INTO `users` VALUES ('patrick', 'patrick@gmail.com', '" . password_hash('merahjambu', PASSWORD_BCRYPT) ."', 0);",
"INSERT INTO `transactions` VALUES ('spongebob', 1, 3, 36000, 'Bikini Bottom yang rumahnya bentuk nanas', '2020-07-23');",
"INSERT INTO `transactions` VALUES ('spongebob', 5, 1, 22000, 'Bikini Bottom yang rumahnya bentuk nanas', '2020-05-17');",
"INSERT INTO `transactions` VALUES ('spongebob', 3, 2, 20000, 'Bikini Bottom yang rumahnya bentuk nanas', '2020-02-21');",
"INSERT INTO `transactions` VALUES ('spongebob', 8, 5, 200000, 'Bikini Bottom yang rumahnya bentuk nanas', '2020-08-16');",
"INSERT INTO `transactions` VALUES ('spongebob', 11, 3, 9000, 'Bikini Bottom yang rumahnya bentuk nanas', '2020-06-18');",
"INSERT INTO `transactions` VALUES ('spongebob', 2, 2, 30000, 'Bikini Bottom yang rumahnya bentuk nanas', '2020-10-29');",
"INSERT INTO `transactions` VALUES ('patrick', 11, 9, 27000, 'Batu', '2020-12-23');",
"INSERT INTO `transactions` VALUES ('patrick', 3, 12, 120000, 'Batu', '2020-05-25');",
"INSERT INTO `transactions` VALUES ('patrick', 4, 15, 270000, 'Batu', '2020-07-21');",
"INSERT INTO `transactions` VALUES ('patrick', 12, 30, 30000, 'Batu', '2020-08-22');",
];
foreach ($dummies as $sql) {
if ($conn->query($sql)){
echo $sql . '<span style="color: green; font-weight: bold;"> EXECUTED PROPERLY</span>';
} else {
echo $sql . '<span style="color: red; font-weight: bold;"> NOT EXECUTED / ALREADY EXIST</span>';
}
echo '<br /><br />';
}
?>
\ No newline at end of file
<?php
define('TRANSACTION_HISTORY_LIMIT', 25);
?>
\ No newline at end of file
<?php
header("location: src/dashboard.php");
exit;
@import "./index.css";
.page-title {
display: flex;
flex-direction: row;
align-items: flex-start;
margin: 1rem 0 3rem;
}
.choco-container {
display: flex;
flex-direction: row;
}
.image-box {
position: relative;
height: 24rem;
box-sizing: border-box;
border: solid 2px var(--primary);
border-radius: 2rem;
}
.image {
height: 100%;
max-width: 400px;
border-radius: 2rem;
object-fit: cover;
display: block;
backface-visibility: hidden;
}
.choco-right {
display: flex;
flex-direction: column;
flex-grow: 1;
justify-content: space-between;
padding: 0 2rem;
}
.choco-props > * {
margin: 1.5rem 0 0;
}
.choco-title {
margin: 0;
}
.choco-desc {
margin: 0.5rem 0;
}
.amount-modifier {
display: flex;
flex-direction: row;
margin-top: 4rem;
}
.mod {
display: flex;
flex-direction: row;
margin-top: 1rem;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
.mod-number {
margin: 0 2rem;
text-align: center;
width: 10rem;
border: solid 2px var(--primary2);
border-radius: 3rem;
}
.total-container {
padding-left: 3rem;
display: flex;
flex-direction: column;
}
.total-display {
display: flex;
flex-grow: 1;
align-items: center;
}
#plus,
#minus {
display: flex;
align-items: center;
justify-content: center;
width: 4rem;
height: 4rem;
text-align: center;
border-radius: 50%;
color: white;
font-size: 30px;
font-weight: 700;
cursor: pointer;
border: solid 4px rgb(0, 161, 86);
}
#plus {
background-color: rgb(35, 198, 108);
border: solid 4px rgb(0, 161, 86);
}
#minus {
background-color: rgb(211, 12, 61);
border: solid 4px rgb(145, 5, 40);
}
.form-bawah {
display: flex;
flex-direction: column;
}
.btn-group {
display: flex;
flex-direction: row;
align-self: flex-end;
flex-grow: 1;
margin-top: 10rem;
}
.btn-group > button {
margin: 0 1rem;
}
.address-block {
display: flex;
flex-direction: column;
}
.address-input {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
outline: none;
overflow: hidden;
resize: none;
border-radius: 1rem;
padding: 5px;
outline: none;
width: 30vw;
margin: 10px 0;
}
.btn-primary {
min-width: 16rem;
padding: 1rem 2rem;
background-color: rgba(255, 60, 100, 1);