first commit
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
node_modules/
|
||||
.next/
|
||||
|
||||
# python
|
||||
__pycache__/
|
||||
.env/
|
||||
1
backend/app.py
Normal file
@ -0,0 +1 @@
|
||||
print("ini try")
|
||||
BIN
backend/berat.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
17
backend/dataset/dataset.csv
Normal file
@ -0,0 +1,17 @@
|
||||
Usia,Berat,Keliling,Ukuran_batang,Jarak_duri,Keterangan
|
||||
14,753,44,4.5,10.9,Masak
|
||||
18,505,42,4,10,Masak
|
||||
17,750,43,3,10,Masak
|
||||
15,755,44,5,10,Masak
|
||||
14,815,45,4.2,10,Masak
|
||||
17,660,41.5,4.5,10,Masak
|
||||
15,700,42,3,10,Masak
|
||||
16,820,46,4,10,Masak
|
||||
12,850,44,4.5,10,Mentah
|
||||
11,830,42,4.2,10,Mentah
|
||||
10,900,42,3,8.5,Mentah
|
||||
9,900,30,2.5,7.5,Mentah
|
||||
9,850,35,3,7.5,Mentah
|
||||
13,800,40,3.9,9,Mentah
|
||||
11,950,41,3.8,8.5,Mentah
|
||||
13,860,40,4,9.5,Mentah
|
||||
|
13
backend/dataset/himpunan_fuzzy.csv
Normal file
@ -0,0 +1,13 @@
|
||||
Fungsi,Nama Variabel,Semesta Pembicaraan, Himpunan Fuzzy,Domain
|
||||
Input,Usia,Sini semesta usia,Rendah,domain rendah usia
|
||||
Input,Usia,Sini semesta usia,Tinggi,domain tinggi usia
|
||||
Input,Berat,Sini semesta berat,Rendah,domain rendah berat
|
||||
Input,Berat,Sini semesta berat,Tinggi,domain tinggi berat
|
||||
Input,Keliling,Sini semesta keliling,Rendah,domain rendah keliling
|
||||
Input,Keliling,Sini semesta keliling,Tinggi,domain tinggi keliling
|
||||
Input,Ukurang Batang,Sini semesta ukuran batang,Rendah,domain rendah ukuran batang
|
||||
Input,Ukurang Batang,Sini semesta ukuran batang,Tinggi,domain tinggi ukuran batang
|
||||
Input,Jarak Duri,Sini semesta jarak duri,Rendah,domain rendah jarak duri
|
||||
Input,Jarak Duri,Sini semesta jarak duri,Tinggi,domain tinggi jarak duri
|
||||
Output,Keterangan,Sini semesta keterangan,Rendah (Mentah),domain rendah keterangan
|
||||
Output,Keterangan,Sini semesta keterangan,Tinggi (Masak),domain tinggi keterangan
|
||||
|
33
backend/dataset/rule.csv
Normal file
@ -0,0 +1,33 @@
|
||||
No,Rule,Usia,Berat,Keliling,Ukuran Batang,Jarak Duri,Keterangan
|
||||
1,R1,Tinggi,Tinggi,Tinggi,Tinggi,Tinggi,Masak
|
||||
2,R2,Tinggi,Tinggi,Tinggi,Tinggi,Rendah,Masak
|
||||
3,R3,Tinggi,Tinggi,Tinggi,Rendah,Tinggi,Masak
|
||||
4,R4,Tinggi,Tinggi,Tinggi,Rendah,Rendah,Masak
|
||||
5,R5,Tinggi,Tinggi,Rendah,Tinggi,Tinggi,Masak
|
||||
6,R6,Tinggi,Tinggi,Rendah,Tinggi,Rendah,Masak
|
||||
7,R7,Tinggi,Tinggi,Rendah,Rendah,Tinggi,Masak
|
||||
8,R8,Tinggi,Tinggi,Rendah,Rendah,Rendah,Mentah
|
||||
9,R9,Tinggi,Rendah,Tinggi,Tinggi,Tinggi,Masak
|
||||
10,R10,Tinggi,Rendah,Tinggi,Tinggi,Rendah,Masak
|
||||
11,R11,Tinggi,Rendah,Tinggi,Rendah,Tinggi,Masak
|
||||
12,R12,Tinggi,Rendah,Tinggi,Rendah,Rendah,Masak
|
||||
13,R13,Tinggi,Rendah,Rendah,Tinggi,Tinggi,Masak
|
||||
14,R14,Tinggi,Rendah,Rendah,Tinggi,Rendah,Masak
|
||||
15,R15,Tinggi,Rendah,Rendah,Rendah,Tinggi,Masak
|
||||
16,R16,Tinggi,Rendah,Rendah,Rendah,Rendah,Masak
|
||||
17,R17,Rendah,Tinggi,Tinggi,Tinggi,Tinggi,Mentah
|
||||
18,R18,Rendah,Tinggi,Tinggi,Tinggi,Rendah,Mentah
|
||||
19,R19,Rendah,Tinggi,Tinggi,Rendah,Tinggi,Mentah
|
||||
20,R20,Rendah,Tinggi,Tinggi,Rendah,Rendah,Mentah
|
||||
21,R21,Rendah,Tinggi,Rendah,Tinggi,Tinggi,Mentah
|
||||
22,R22,Rendah,Tinggi,Rendah,Tinggi,Rendah,Mentah
|
||||
23,R23,Rendah,Tinggi,Rendah,Rendah,Tinggi,Mentah
|
||||
24,R24,Rendah,Tinggi,Rendah,Rendah,Rendah,Mentah
|
||||
25,R25,Rendah,Rendah,Tinggi,Tinggi,Tinggi,Masak
|
||||
26,R26,Rendah,Rendah,Tinggi,Tinggi,Rendah,Mentah
|
||||
27,R27,Rendah,Rendah,Tinggi,Rendah,Tinggi,Mentah
|
||||
28,R28,Rendah,Rendah,Tinggi,Rendah,Rendah,Mentah
|
||||
29,R29,Rendah,Rendah,Rendah,Tinggi,Tinggi,Mentah
|
||||
30,R30,Rendah,Rendah,Rendah,Tinggi,Rendah,Mentah
|
||||
31,R31,Rendah,Rendah,Rendah,Rendah,Tinggi,Mentah
|
||||
32,R32,Rendah,Rendah,Rendah,Rendah,Rendah,Mentah
|
||||
|
347
backend/fuzzy.py
Normal file
@ -0,0 +1,347 @@
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import csv
|
||||
import skfuzzy as fuzz
|
||||
|
||||
import asyncio
|
||||
|
||||
async def contoh_dulu(data_durian,anggota_usia,anggota_berat,anggota_keliling,anggota_ukuran_batang,anggota_jarak_duri,i):
|
||||
# hitungan fuzzy tsukamoto berdasarkan rule base
|
||||
df = pd.read_csv("dataset/rule.csv")
|
||||
rule_length = len(df.index)
|
||||
list_rule = df
|
||||
hitungan_fuzzy_tsukamoto = []
|
||||
for e in range(i):
|
||||
ket_usia = "Tinggi" if anggota_usia[e][0] < anggota_usia[e][1] else "Rendah"
|
||||
ket_berat = "Tinggi" if anggota_berat[e][0] < anggota_berat[e][1] else "Rendah"
|
||||
ket_keliling = "Tinggi" if anggota_keliling[e][0] <= anggota_keliling[e][1] else "Rendah"
|
||||
ket_ukuran_batang = "Tinggi" if anggota_ukuran_batang[e][0] <= anggota_ukuran_batang[e][1] else "Rendah"
|
||||
ket_jarak_duri = "Tinggi" if anggota_jarak_duri[e][0] < anggota_jarak_duri[e][1] else "Rendah"
|
||||
|
||||
keterangan = None
|
||||
for n in range(rule_length):
|
||||
if str(list_rule.at[n,'Usia']) == ket_usia and str(list_rule.at[n,'Berat']) == ket_berat and str(list_rule.at[n,'Keliling']) == ket_keliling and str(list_rule.at[n,'Ukuran Batang']) == ket_ukuran_batang and str(list_rule.at[n,'Jarak Duri']) == ket_jarak_duri:
|
||||
keterangan = str(list_rule.at[n,'Keterangan'])
|
||||
|
||||
data = {"No" : e, "Usia" : ket_usia, "Berat" : ket_berat, "Keliling" : ket_keliling,
|
||||
"Ukuran Batang" : ket_ukuran_batang, "Jarak Duri" : ket_jarak_duri ,"Variable Linguistic" : keterangan,
|
||||
"Keterangan" : data_durian.at[e,'Keterangan'], "MSE" : 0 if keterangan == data_durian.at[e,'Keterangan'] else 1
|
||||
}
|
||||
hitungan_fuzzy_tsukamoto.append(data)
|
||||
await asyncio.sleep(0.1)
|
||||
return(hitungan_fuzzy_tsukamoto)
|
||||
|
||||
|
||||
def FungsiKeanggotaan(_range, _min , _hi, _nilai):
|
||||
mini = fuzz.interp_membership(_range,_min,_nilai)
|
||||
hi = fuzz.interp_membership(_range,_hi,_nilai)
|
||||
# await asyncio.sleep(0.1)
|
||||
return mini , hi
|
||||
|
||||
def RangeSubjektif(_low, _high, _step):
|
||||
subjektif = np.arange(_low, _high , _step)
|
||||
return subjektif
|
||||
|
||||
def FuzzyShow(_rule, _range_subjektif, _title, _pic_title):
|
||||
lo = fuzz.trapmf(_range_subjektif, _rule[0])
|
||||
hi = fuzz.trapmf(_range_subjektif, _rule[1])
|
||||
|
||||
fig,ax = plt.subplots(nrows=1, figsize=(7,3))
|
||||
ax.plot(_range_subjektif, lo, 'g' , linewidth = 1.5 , label= "Mentah")
|
||||
ax.plot(_range_subjektif, hi, 'r' , linewidth = 1.5 , label= "Masak")
|
||||
|
||||
ax.set_title(_title)
|
||||
ax.legend()
|
||||
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.get_xaxis().tick_bottom()
|
||||
ax.get_yaxis().tick_left()
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig(_pic_title+'.png')
|
||||
# plt.show()
|
||||
|
||||
|
||||
return lo, hi
|
||||
|
||||
def FuzzyShow1(_rule, _range_subjektif, _title,_pic_title):
|
||||
lo = fuzz.trapmf(_range_subjektif, _rule[0])
|
||||
hi = fuzz.trapmf(_range_subjektif, _rule[1])
|
||||
|
||||
fig,ax = plt.subplots(nrows=1, figsize=(7,3))
|
||||
ax.plot(_range_subjektif, lo, 'r' , linewidth = 1.5 , label= "Masak")
|
||||
ax.plot(_range_subjektif, hi, 'g' , linewidth = 1.5 , label= "Mentah")
|
||||
|
||||
ax.set_title(_title)
|
||||
ax.legend()
|
||||
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.get_xaxis().tick_bottom()
|
||||
ax.get_yaxis().tick_left()
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig(_pic_title+'.png')
|
||||
# plt.show()
|
||||
|
||||
|
||||
return lo, hi
|
||||
|
||||
def get_average(min,max) :
|
||||
a = (min + max) / 2
|
||||
return a
|
||||
|
||||
async def himpunan_fuzzy(min_usia,mid_usia,max_usia,min_berat,mid_berat,max_berat,min_keliling,mid_keliling,max_keliling,min_ukuran_batang,mid_ukuran_batang,max_ukuran_batang,min_jarak_duri,mid_jarak_duri,max_jarak_duri):
|
||||
a = None
|
||||
himpunan_fuzzy = pd.read_csv("dataset/himpunan_fuzzy.csv")
|
||||
himpunan_fuzzy.loc[0, 'Semesta Pembicaraan'] = f"[ {min_usia} , {max_usia} ]" # Semesta pembicaraan (Rendah) Usia
|
||||
himpunan_fuzzy.loc[1, 'Semesta Pembicaraan'] = f"[ {min_usia} , {max_usia} ]" # Semesta pembicaraan (Tinggi) Usia
|
||||
himpunan_fuzzy.loc[0, 'Domain'] = f"[ {min_usia} , {mid_usia + 0.5} ]" # Domain (Rendah) Usia
|
||||
himpunan_fuzzy.loc[1, 'Domain'] = f"[ {mid_usia - 0.5} , {max_usia} ]" # Domain pembicaraan (Tinggi) Usia
|
||||
|
||||
# Fuzzykasi Berat
|
||||
himpunan_fuzzy.loc[2, 'Semesta Pembicaraan'] = f"[ {min_berat} , {max_berat} ]" # Semesta pembicaraan (Rendah) Berat
|
||||
himpunan_fuzzy.loc[3, 'Semesta Pembicaraan'] = f"[ {min_berat} , {max_berat} ]" # Semesta pembicaraan (Tinggi) Berat
|
||||
himpunan_fuzzy.loc[2, 'Domain'] = f"[ {min_berat} , {mid_berat + 10} ]" # Domain (Rendah) Berat
|
||||
himpunan_fuzzy.loc[3, 'Domain'] = f"[ {mid_berat - 10} , {max_berat} ]" # Domain pembicaraan (Tinggi) Berat
|
||||
|
||||
# Fuzzykasi Keliling
|
||||
himpunan_fuzzy.loc[4, 'Semesta Pembicaraan'] = f"[ {min_keliling} , {max_keliling} ]" # Semesta pembicaraan (Rendah) Keliling
|
||||
himpunan_fuzzy.loc[5, 'Semesta Pembicaraan'] = f"[ {min_keliling} , {max_keliling} ]" # Semesta pembicaraan (Tinggi) Keliling
|
||||
himpunan_fuzzy.loc[4, 'Domain'] = f"[ {min_keliling} , {mid_keliling + 1} ]" # Domain (Rendah) Keliling
|
||||
himpunan_fuzzy.loc[5, 'Domain'] = f"[ {mid_keliling - 1} , {max_keliling} ]" # Domain pembicaraan (Tinggi) Keliling
|
||||
|
||||
# Fuzzykasi Keliling
|
||||
himpunan_fuzzy.loc[4, 'Semesta Pembicaraan'] = f"[ {min_keliling} , {max_keliling} ]" # Semesta pembicaraan (Rendah) Keliling
|
||||
himpunan_fuzzy.loc[5, 'Semesta Pembicaraan'] = f"[ {min_keliling} , {max_keliling} ]" # Semesta pembicaraan (Tinggi) Keliling
|
||||
himpunan_fuzzy.loc[4, 'Domain'] = f"[ {min_keliling} , {mid_keliling + 1} ]" # Domain (Rendah) Keliling
|
||||
himpunan_fuzzy.loc[5, 'Domain'] = f"[ {mid_keliling - 1} , {max_keliling} ]" # Domain pembicaraan (Tinggi) Keliling
|
||||
|
||||
# Fuzzykasi Ukuran Batang
|
||||
himpunan_fuzzy.loc[6, 'Semesta Pembicaraan'] = f"[ {min_ukuran_batang} , {max_ukuran_batang} ]" # Semesta pembicaraan (Rendah) Ukuran Batang
|
||||
himpunan_fuzzy.loc[7, 'Semesta Pembicaraan'] = f"[ {min_ukuran_batang} , {max_ukuran_batang} ]" # Semesta pembicaraan (Tinggi) Ukuran Batang
|
||||
himpunan_fuzzy.loc[6, 'Domain'] = f"[ {min_ukuran_batang} , {mid_ukuran_batang + 0.5} ]" # Domain (Rendah) Ukuran Batang
|
||||
himpunan_fuzzy.loc[7, 'Domain'] = f"[ {mid_ukuran_batang - 0.5} , {max_ukuran_batang} ]" # Domain pembicaraan (Tinggi) Ukuran Batang
|
||||
|
||||
# Fuzzykasi Jarak Duri
|
||||
himpunan_fuzzy.loc[8, 'Semesta Pembicaraan'] = f"[ {min_jarak_duri} , {max_jarak_duri} ]" # Semesta pembicaraan (Rendah) Jarak Duri
|
||||
himpunan_fuzzy.loc[9, 'Semesta Pembicaraan'] = f"[ {min_jarak_duri} , {max_jarak_duri} ]" # Semesta pembicaraan (Tinggi) Jarak Duri
|
||||
himpunan_fuzzy.loc[8, 'Domain'] = f"[ {min_jarak_duri} , {mid_jarak_duri + 0.75} ]" # Domain (Rendah) Jarak Duri
|
||||
himpunan_fuzzy.loc[9, 'Domain'] = f"[ {mid_jarak_duri - 0.25} , {max_jarak_duri} ]" # Domain pembicaraan (Tinggi) Jarak Duri
|
||||
|
||||
# Fuzzykasi Keterangan
|
||||
himpunan_fuzzy.loc[10, 'Semesta Pembicaraan'] = f"[ 0 , 1 ]" # Semesta pembicaraan (Rendah) Keterangan
|
||||
himpunan_fuzzy.loc[11, 'Semesta Pembicaraan'] = f"[ 0 , 1 ]" # Semesta pembicaraan (Tinggi) Keterangan
|
||||
himpunan_fuzzy.loc[10, 'Domain'] = f"[ 0 , 0.5 ]" # Domain (Rendah) Keterangan
|
||||
himpunan_fuzzy.loc[11, 'Domain'] = f"[ 0.5 , 1 ]" # Domain pembicaraan (Tinggi) Keterangan
|
||||
|
||||
# print(himpunan_fuzzy.to_dict())
|
||||
himpunan_fuzzy.to_csv('out.csv',index=False)
|
||||
|
||||
with open("out.csv", "r") as f:
|
||||
reader = csv.DictReader(f)
|
||||
a = list(reader)
|
||||
await asyncio.sleep(0.2)
|
||||
# print(a)
|
||||
return a
|
||||
|
||||
async def fuzzy(status, usianya = None , beratnya = None , kelilingnya = None , ukuran_batangnya = None , jarak_durinya = None):
|
||||
dataset = None
|
||||
data_durian = pd.read_csv("dataset/dataset.csv")
|
||||
# await asyncio.sleep(0.5)
|
||||
with open("dataset/dataset.csv", "r") as f:
|
||||
reader = csv.DictReader(f)
|
||||
dataset = list(reader)
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
# data usia
|
||||
data_usia = pd.DataFrame(data_durian)
|
||||
data_usia = data_usia['Usia'].tolist()
|
||||
_data_usia = data_usia
|
||||
min_usia = min(data_usia) - 1
|
||||
max_usia = max(data_usia) +1
|
||||
mid_usia =np.median(data_usia)
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
# data berat
|
||||
data_berat = pd.DataFrame(data_durian)
|
||||
data_berat = data_berat['Berat'].tolist()
|
||||
_data_berat = data_berat
|
||||
min_berat = min(data_berat) - 50
|
||||
max_berat = max(data_berat) + 50
|
||||
mid_berat =np.median(data_berat)
|
||||
|
||||
# data keliling
|
||||
data_keliling = pd.DataFrame(data_durian)
|
||||
data_keliling = data_keliling['Keliling'].tolist()
|
||||
_data_keliling = data_keliling
|
||||
min_keliling = min(data_keliling) - 1
|
||||
max_keliling = max(data_keliling) + 1
|
||||
# mid_keliling = get_average(min_keliling,max_keliling) #42.0
|
||||
mid_keliling =np.median(data_keliling)
|
||||
|
||||
# data ukuran batang
|
||||
data_ukuran_batang = pd.DataFrame(data_durian)
|
||||
data_ukuran_batang = data_ukuran_batang['Ukuran_batang'].tolist()
|
||||
_data_ukuran_batang = data_ukuran_batang
|
||||
min_ukuran_batang = min(data_ukuran_batang) - 0.5
|
||||
max_ukuran_batang = max(data_ukuran_batang) + 0.5
|
||||
# mid_ukuran_batang = get_average(min_ukuran_batang,max_ukuran_batang) #4.0
|
||||
mid_ukuran_batang = np.median(data_ukuran_batang)
|
||||
|
||||
# data jarak duri
|
||||
data_jarak_duri = pd.DataFrame(data_durian)
|
||||
data_jarak_duri = data_jarak_duri['Jarak_duri'].tolist()
|
||||
_data_jarak_duri = data_jarak_duri
|
||||
# print(data_jarak_duri)
|
||||
min_jarak_duri = min(data_jarak_duri) - 0.5
|
||||
max_jarak_duri = max(data_jarak_duri) + 0.5
|
||||
mid_jarak_duri = get_average(min_jarak_duri,max_jarak_duri)#1.0
|
||||
|
||||
|
||||
var_himpunan_fuzzy = await himpunan_fuzzy(min_usia,mid_usia,max_usia,min_berat,mid_berat,max_berat,min_keliling,mid_keliling,max_keliling,min_ukuran_batang,mid_ukuran_batang,max_ukuran_batang,min_jarak_duri,mid_jarak_duri,max_jarak_duri)
|
||||
|
||||
|
||||
# fuzzy untuk field usia
|
||||
x_usia = RangeSubjektif(min_usia , max_usia , 1)
|
||||
r_usia = np.array([
|
||||
[min_usia,min_usia,mid_usia,mid_usia],
|
||||
[mid_usia,mid_usia,max_usia,max_usia]
|
||||
])
|
||||
lo_usia , hi_usia = FuzzyShow(r_usia , x_usia, 'Umur (minggu)',"usia")
|
||||
|
||||
|
||||
# fuzzy untuk field berat
|
||||
x_berat = RangeSubjektif(min_berat , max_berat , 1)
|
||||
r_berat = np.array([
|
||||
[min_berat,min_berat,mid_berat,mid_berat+10],
|
||||
[mid_berat,mid_berat+10,max_berat,max_berat]
|
||||
])
|
||||
lo_berat , hi_berat = FuzzyShow1(r_berat , x_berat, 'Berat (kg)', "berat")
|
||||
|
||||
# fuzzy untuk field keliling
|
||||
x_keliling = RangeSubjektif(min_keliling , max_keliling , 1)
|
||||
r_keliling = np.array([
|
||||
[min_keliling,min_keliling,mid_keliling,mid_keliling],
|
||||
[mid_keliling,mid_keliling,max_keliling,max_keliling]
|
||||
])
|
||||
lo_keliling , hi_keliling = FuzzyShow(r_keliling , x_keliling, 'Keliling (cm)', "keliling")
|
||||
|
||||
# fuzzy untuk field ukuran batang
|
||||
x_ukuran_batang = RangeSubjektif(min_ukuran_batang , max_ukuran_batang , 1)
|
||||
r_ukuran_batang = np.array([
|
||||
[min_ukuran_batang,min_ukuran_batang,mid_ukuran_batang,mid_ukuran_batang],
|
||||
[mid_ukuran_batang,mid_ukuran_batang,max_ukuran_batang,max_ukuran_batang]
|
||||
])
|
||||
lo_ukuran_batang , hi_ukuran_batang = FuzzyShow(r_ukuran_batang , x_ukuran_batang, 'Keliling (cm)', "ukuran_batang")
|
||||
|
||||
# fuzzy untuk field jarak duri
|
||||
x_jarak_duri = RangeSubjektif(min_jarak_duri , max_jarak_duri , 1)
|
||||
r_jarak_duri = np.array([
|
||||
[min_jarak_duri,min_jarak_duri,mid_jarak_duri,mid_jarak_duri],
|
||||
[mid_jarak_duri,mid_jarak_duri,max_jarak_duri,max_jarak_duri]
|
||||
])
|
||||
lo_jarak_duri , hi_jarak_duri = FuzzyShow(r_jarak_duri , x_jarak_duri, 'Jarak Duri (mm)', "jarak_duri")
|
||||
|
||||
|
||||
# Keanggotaan untuk usia
|
||||
i = 0
|
||||
anggota_usia = []
|
||||
for usia in _data_usia:
|
||||
ini_dia = FungsiKeanggotaan(x_usia,lo_usia,hi_usia,usia)
|
||||
|
||||
anggota_usia.append(ini_dia)
|
||||
i = i+1
|
||||
# print(anggota_usia)
|
||||
|
||||
# Keanggotaan untuk berat
|
||||
anggota_berat = []
|
||||
for berat in _data_berat:
|
||||
ini_dia = FungsiKeanggotaan(x_berat,lo_berat,hi_berat,berat)
|
||||
anggota_berat.append(ini_dia)
|
||||
|
||||
# Keanggotaan untuk keliling
|
||||
anggota_keliling = []
|
||||
for keliling in _data_keliling:
|
||||
ini_dia = FungsiKeanggotaan(x_keliling,lo_keliling,hi_keliling,keliling)
|
||||
anggota_keliling.append(ini_dia)
|
||||
|
||||
# Keanggotaan untuk ukuran batang
|
||||
anggota_ukuran_batang = []
|
||||
for ukuran_batang in _data_ukuran_batang:
|
||||
ini_dia = FungsiKeanggotaan(x_ukuran_batang,lo_ukuran_batang,hi_ukuran_batang,ukuran_batang)
|
||||
# print(ini_dia)
|
||||
anggota_ukuran_batang.append(ini_dia)
|
||||
|
||||
# Keanggotaan untuk jarak duri
|
||||
anggota_jarak_duri = []
|
||||
for jarak_duri in _data_jarak_duri:
|
||||
ini_dia = FungsiKeanggotaan(x_jarak_duri,lo_jarak_duri,hi_jarak_duri,jarak_duri)
|
||||
anggota_jarak_duri.append(ini_dia)
|
||||
|
||||
rule_base = None
|
||||
with open("dataset/rule.csv", "r") as f:
|
||||
reader = csv.DictReader(f)
|
||||
rule_base = list(reader)
|
||||
|
||||
|
||||
keanggotaannya = await contoh_dulu(data_durian,anggota_usia,anggota_berat,anggota_keliling,anggota_ukuran_batang,anggota_jarak_duri,i)
|
||||
|
||||
# print(keanggotaannya)
|
||||
data_usia = {'min':min_usia, 'max':max_usia}
|
||||
data_berat = {'min':min_berat, 'max':max_berat}
|
||||
data_keliling = {'min':min_keliling, 'max':max_keliling}
|
||||
data_ukuran_batang = {'min':min_ukuran_batang, 'max':max_ukuran_batang}
|
||||
data_jarak_duri = {'min':min_jarak_duri, 'max':max_jarak_duri}
|
||||
detail_attribut = {'usia':data_usia, 'berat':data_berat, 'keliling':data_keliling, 'ukuran_batang':data_ukuran_batang, 'jarak_duri':data_jarak_duri}
|
||||
|
||||
if(status == 'ambil_dataset'):
|
||||
context = {'detail_attribute':detail_attribut, 'rule_base':rule_base, 'keanggotaan':keanggotaannya, 'dataset' : dataset, 'himpunan_fuzzy' : var_himpunan_fuzzy}
|
||||
|
||||
elif (status == 'load_fuzzy'):
|
||||
def hitungan_keterangan(usia,berat,keliling,ukuran_batang,jarak_duri):
|
||||
df = pd.read_csv("dataset/rule.csv")
|
||||
rule_length = len(df.index)
|
||||
list_rule = df
|
||||
keterangan = None
|
||||
ket_usia = "Tinggi" if usia[0] < usia[1] else "Rendah"
|
||||
ket_berat = "Tinggi" if berat[0] < berat[1] else "Rendah"
|
||||
ket_keliling = "Tinggi" if keliling[0] <= keliling[1] else "Rendah"
|
||||
ket_ukuran_batang = "Tinggi" if ukuran_batang[0] <= ukuran_batang[1] else "Rendah"
|
||||
ket_jarak_duri = "Tinggi" if jarak_duri[0] < jarak_duri[1] else "Rendah"
|
||||
for n in range(rule_length):
|
||||
if str(list_rule.at[n,'Usia']) == ket_usia and str(list_rule.at[n,'Berat']) == ket_berat and str(list_rule.at[n,'Keliling']) == ket_keliling and str(list_rule.at[n,'Ukuran Batang']) == ket_ukuran_batang and str(list_rule.at[n,'Jarak Duri']) == ket_jarak_duri:
|
||||
keterangan = str(list_rule.at[n,'Keterangan'])
|
||||
break
|
||||
return keterangan
|
||||
|
||||
usia = usianya
|
||||
berat = beratnya
|
||||
keliling = kelilingnya
|
||||
ukuran_batang = ukuran_batangnya
|
||||
jarak_duri = jarak_durinya
|
||||
keanggotaan_usia = FungsiKeanggotaan(x_usia,lo_usia,hi_usia,usia)
|
||||
print(keanggotaan_usia)
|
||||
keanggotaan_berat = FungsiKeanggotaan(x_berat,lo_berat,hi_berat,berat)
|
||||
print(keanggotaan_berat)
|
||||
keanggotaan_keliling = FungsiKeanggotaan(x_keliling,lo_keliling,hi_keliling,keliling)
|
||||
print(keanggotaan_keliling)
|
||||
keanggotaan_ukuran_batang = FungsiKeanggotaan(x_ukuran_batang,lo_ukuran_batang,hi_ukuran_batang,ukuran_batang)
|
||||
print(keanggotaan_ukuran_batang)
|
||||
keanggotaan_jarak_duri =FungsiKeanggotaan(x_jarak_duri,lo_jarak_duri,hi_jarak_duri,jarak_duri)
|
||||
print(keanggotaan_jarak_duri)
|
||||
datanya = hitungan_keterangan(keanggotaan_usia,keanggotaan_berat,keanggotaan_keliling,keanggotaan_ukuran_batang,keanggotaan_jarak_duri)
|
||||
context = datanya
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# hitungan fuzzy tsukamoto berdasarkan data latih
|
||||
|
||||
return context
|
||||
BIN
backend/jarak_duri.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
backend/keliling.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
67
backend/main.py
Normal file
@ -0,0 +1,67 @@
|
||||
# from typing import Optional
|
||||
import json
|
||||
import os
|
||||
from fastapi import FastAPI, Form, UploadFile , File , Request,HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fuzzy import fuzzy
|
||||
# import asyncio
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
|
||||
# create the fastapi api
|
||||
@app.get("/")
|
||||
async def read_root():
|
||||
dataset = await fuzzy("ambil_dataset")
|
||||
# dataset.update({"image_usia":FileResponse(image_usia)})
|
||||
return dataset
|
||||
|
||||
# create post request
|
||||
@app.post("/")
|
||||
async def read_root_post(request: Request):
|
||||
body = await request.form()
|
||||
if body:
|
||||
# check if body['data'] is not empty and not None
|
||||
if body['data'] is not None and body['data'] != "":
|
||||
json_data = json.loads(body['data'])
|
||||
|
||||
datanya = await fuzzy("load_fuzzy", json_data['usia'], json_data['berat'], json_data['keliling'], json_data['ukuran_batang'], json_data['jarak_duri'])
|
||||
# print(datanya)
|
||||
return {"ket": datanya}
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail="data is empty")
|
||||
else:
|
||||
# print("ko")
|
||||
raise HTTPException(status_code=404, detail="error")
|
||||
# return {"message": "sini post datanya"}
|
||||
|
||||
# creata a image get
|
||||
@app.get("/image/{image_name}")
|
||||
async def read_image(image_name: str):
|
||||
file_path = os.path.join("", image_name+'.png')
|
||||
if os.path.exists(file_path):
|
||||
return FileResponse(file_path)
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail="File not found")
|
||||
# image = await FileResponse(image_name+".png")
|
||||
# return image
|
||||
|
||||
|
||||
# create post method /fuzzy with form data and return the data
|
||||
# @app.post("/fuzzy")
|
||||
# async def read_item(nama: str = Form(...),
|
||||
# umur: int = Form(...),
|
||||
# alamat: str = Form(...),
|
||||
# foto : UploadFile = File(...)):
|
||||
# return {"nama": nama, "umur": umur, "alamat": alamat, "foto": foto}
|
||||
|
||||
13
backend/out.csv
Normal file
@ -0,0 +1,13 @@
|
||||
Fungsi,Nama Variabel,Semesta Pembicaraan, Himpunan Fuzzy,Domain
|
||||
Input,Usia,"[ 8 , 19 ]",Rendah,"[ 8 , 14.0 ]"
|
||||
Input,Usia,"[ 8 , 19 ]",Tinggi,"[ 13.0 , 19 ]"
|
||||
Input,Berat,"[ 455 , 1000 ]",Rendah,"[ 455 , 827.5 ]"
|
||||
Input,Berat,"[ 455 , 1000 ]",Tinggi,"[ 807.5 , 1000 ]"
|
||||
Input,Keliling,"[ 29.0 , 47.0 ]",Rendah,"[ 29.0 , 43.0 ]"
|
||||
Input,Keliling,"[ 29.0 , 47.0 ]",Tinggi,"[ 41.0 , 47.0 ]"
|
||||
Input,Ukurang Batang,"[ 2.0 , 5.5 ]",Rendah,"[ 2.0 , 4.5 ]"
|
||||
Input,Ukurang Batang,"[ 2.0 , 5.5 ]",Tinggi,"[ 3.5 , 5.5 ]"
|
||||
Input,Jarak Duri,"[ 7.0 , 11.4 ]",Rendah,"[ 7.0 , 9.95 ]"
|
||||
Input,Jarak Duri,"[ 7.0 , 11.4 ]",Tinggi,"[ 8.95 , 11.4 ]"
|
||||
Output,Keterangan,"[ 0 , 1 ]",Rendah (Mentah),"[ 0 , 0.5 ]"
|
||||
Output,Keterangan,"[ 0 , 1 ]",Tinggi (Masak),"[ 0.5 , 1 ]"
|
||||
|
BIN
backend/requirements.txt
Normal file
BIN
backend/ukuran_batang.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
backend/usia.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
3
frontend/.eslintrc.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
34
frontend/README.md
Normal file
@ -0,0 +1,34 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
239
frontend/components/halaman/DataSet.js
Normal file
@ -0,0 +1,239 @@
|
||||
import classess from "./InputData.module.css";
|
||||
import Card from "../ui/Card";
|
||||
|
||||
function DataSet(props) {
|
||||
let status = props.data.status;
|
||||
let table_dataset,himpunan_fuzzy,url,table_rule_base,table_keanggotaan
|
||||
if (status == "error") {
|
||||
console.log("error")
|
||||
}else{
|
||||
console.log(props.data)
|
||||
let dataset = props.data.dataset;
|
||||
let rule_base = props.data.rule_base;
|
||||
let keanggotaan = props.data.keanggotaan;
|
||||
// create a variable table_dataset table from dataset where first column is id, second column is Usia("Minggu"), third column is Berat("gram"), fourth column is Keliling(cm), fifth column is Ukuran Batang(cm), sixth column is Jarak Duri(mm)
|
||||
table_dataset = dataset.map((item, index) => {
|
||||
return (
|
||||
<tr key={index}>
|
||||
<td>{index}</td>
|
||||
<td>{item.Usia}</td>
|
||||
<td>{item.Berat}</td>
|
||||
<td>{item.Keliling}</td>
|
||||
<td>{item.Ukuran_batang}</td>
|
||||
<td>{item.Jarak_duri}</td>
|
||||
<td>{item.Keterangan}</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
|
||||
himpunan_fuzzy = props.data.himpunan_fuzzy;
|
||||
url = "http://127.0.0.1:5000/";
|
||||
|
||||
table_rule_base = rule_base.map((item, index) => {
|
||||
return (
|
||||
<tr key={index}>
|
||||
<td>{item.No}</td>
|
||||
<td>{item.Rule}</td>
|
||||
<td>{item.Usia}</td>
|
||||
<td>{item.Berat}</td>
|
||||
<td>{item.Keliling}</td>
|
||||
<td>{item['Ukuran Batang']}</td>
|
||||
<td>{item['Jarak Duri']}</td>
|
||||
<td>{item.Keterangan}</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
|
||||
table_keanggotaan = keanggotaan.map((item, index) => {
|
||||
return (
|
||||
<tr key={index}>
|
||||
<td>{item.No}</td>
|
||||
<td>{item.Usia}</td>
|
||||
<td>{item.Berat}</td>
|
||||
<td>{item.Keliling}</td>
|
||||
<td>{item['Ukuran Batang']}</td>
|
||||
<td>{item['Jarak Duri']}</td>
|
||||
<td>{item['Variable Linguistic']}</td>
|
||||
<td>{item.Keterangan}</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<form className={classess.form} >
|
||||
<div className={classess.control}>
|
||||
<h2 htmlFor="title">DataSet</h2>
|
||||
<div id="div_dataset" >
|
||||
<table id="table_dataset" className={classess.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Usia <br /> ( <i>Minggu</i> )</th>
|
||||
<th>Berat <br /> ( <i>gram</i> )</th>
|
||||
<th>Keliling <br /> ( <i>cm</i> ) </th>
|
||||
<th>Ukuran Batang <br /> ( <i>cm</i> ) </th>
|
||||
<th>Jarak Duri <br /> ( <i>mm</i> ) </th>
|
||||
<th>Keterangan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{table_dataset}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<hr />
|
||||
<div className={classess.control}>
|
||||
<h2 htmlFor="image">Semesta Pembicaraan</h2>
|
||||
<div id="div_himpunan_fuzzy" >
|
||||
<table id="table_himpunan_fuzzy" className={classess.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Fungsi</th>
|
||||
<th>Nama Variabel</th>
|
||||
<th>Semesta Pembicaraan</th>
|
||||
<th>Himpunan Fuzzy</th>
|
||||
<th>Domain</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowSpan={10}>Input</td>
|
||||
<td rowSpan={2}>Usia <br /> <i>(Minggu)</i> </td>
|
||||
<td rowSpan={2}> <b><i>{himpunan_fuzzy[0]['Semesta Pembicaraan']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[0][' Himpunan Fuzzy']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[0]['Domain']}</i></b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> <b><i>{himpunan_fuzzy[1][' Himpunan Fuzzy']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[1]['Domain']}</i></b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowSpan={2}>Berat <br /> <i>(gram)</i></td>
|
||||
<td rowSpan={2}> <b><i>{himpunan_fuzzy[2]['Semesta Pembicaraan']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[2][' Himpunan Fuzzy']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[2]['Domain']}</i></b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> <b><i>{himpunan_fuzzy[3][' Himpunan Fuzzy']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[3]['Domain']}</i></b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowSpan={2}>Keliling <br /> <i>(cm)</i></td>
|
||||
<td rowSpan={2}> <b><i>{himpunan_fuzzy[4]['Semesta Pembicaraan']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[4][' Himpunan Fuzzy']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[4]['Domain']}</i></b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> <b><i>{himpunan_fuzzy[5][' Himpunan Fuzzy']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[5]['Domain']}</i></b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowSpan={2}>Ukuran Batang <br /> <i>(cm)</i></td>
|
||||
<td rowSpan={2}> <b><i>{himpunan_fuzzy[6]['Semesta Pembicaraan']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[6][' Himpunan Fuzzy']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[6]['Domain']}</i></b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> <b><i>{himpunan_fuzzy[7][' Himpunan Fuzzy']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[7]['Domain']}</i></b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowSpan={2}>Jarak Duri <br /> <i>(mm)</i></td>
|
||||
<td rowSpan={2}> <b><i>{himpunan_fuzzy[8]['Semesta Pembicaraan']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[8][' Himpunan Fuzzy']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[8]['Domain']}</i></b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> <b><i>{himpunan_fuzzy[9][' Himpunan Fuzzy']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[9]['Domain']}</i></b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowSpan={2}>Output</td>
|
||||
<td rowSpan={2}>Keterangan</td>
|
||||
<td rowSpan={2}> <b><i>{himpunan_fuzzy[10]['Semesta Pembicaraan']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[10][' Himpunan Fuzzy']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[10]['Domain']}</i></b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> <b><i>{himpunan_fuzzy[11][' Himpunan Fuzzy']}</i></b> </td>
|
||||
<td> <b><i>{himpunan_fuzzy[11]['Domain']}</i></b> </td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<br /><hr />
|
||||
<div className={classess.control}>
|
||||
<h2 htmlFor="address">Himpunan <i>Fuzzy</i></h2>
|
||||
<center><h3><i>Fuzzy</i> Usia</h3></center>
|
||||
<img src={url+"image/usia"} className={classess.image} />
|
||||
<center><h3><i>Fuzzy</i> Berat</h3></center>
|
||||
<img src={url+"image/berat"} className={classess.image} />
|
||||
<center><h3><i>Fuzzy</i> Keliling</h3></center>
|
||||
<img src={url+"image/keliling"} className={classess.image} />
|
||||
<center><h3><i>Fuzzy</i> Ukuran Batang</h3></center>
|
||||
<img src={url+"image/ukuran_batang"} className={classess.image} />
|
||||
<center><h3><i>Fuzzy</i> Jarak Duri</h3></center>
|
||||
<img src={url+"image/jarak_duri"} className={classess.image} />
|
||||
</div>
|
||||
<br /><hr />
|
||||
<div className={classess.control}>
|
||||
<h2 htmlFor="description">Rule Base</h2>
|
||||
<div id="div_rule_base" >
|
||||
<table className={classess.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Rule</th>
|
||||
<th>Usia</th>
|
||||
<th>Berat</th>
|
||||
<th>Keliling</th>
|
||||
<th>Ukuran Batang</th>
|
||||
<th>Jarak Duri</th>
|
||||
<th>Keterangan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{table_rule_base}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<br /><hr />
|
||||
<div className={classess.control}>
|
||||
<h2 htmlFor="description">Keanggotaan Data</h2>
|
||||
<div id="div_keanggotaan" >
|
||||
<table className={classess.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Usia</th>
|
||||
<th>Berat</th>
|
||||
<th>Keliling</th>
|
||||
<th>Ukuran <br /> Batang</th>
|
||||
<th>Jarak <br /> Duri</th>
|
||||
<th>Variable <br />Linguistic</th>
|
||||
<th>Keterangan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{table_keanggotaan}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default DataSet;
|
||||
177
frontend/components/halaman/InputData.js
Normal file
@ -0,0 +1,177 @@
|
||||
import { useRef, useState } from "react"; // use refs for reading value
|
||||
|
||||
import classess from "./InputData.module.css";
|
||||
import Card from "../ui/Card";
|
||||
|
||||
import { ToastContainer ,toast , Zoom , Bounce } from 'react-toastify'
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
import Backdrop from '@mui/material/Backdrop';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
|
||||
import Swal from 'sweetalert2'
|
||||
import withReactContent from 'sweetalert2-react-content'
|
||||
|
||||
const MySwal = withReactContent(Swal)
|
||||
|
||||
function InputData(props) {
|
||||
let datanya = props.data;
|
||||
// console.log(datanya);
|
||||
|
||||
const usiaInputRef = useRef();
|
||||
const beratInputRef = useRef();
|
||||
const kelilingInputRef = useRef();
|
||||
const ukuranBatangInputRef = useRef();
|
||||
const jarakDuriInputRef = useRef();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
async function submitHandler(event) {
|
||||
event.preventDefault(); // ini untuk prevent reload atau submit default
|
||||
|
||||
const usia = usiaInputRef.current.value;
|
||||
const berat = beratInputRef.current.value;
|
||||
const keliling = kelilingInputRef.current.value;
|
||||
const ukuran_batang = ukuranBatangInputRef.current.value;
|
||||
const jarak_duri = jarakDuriInputRef.current.value;
|
||||
|
||||
|
||||
if(usia < datanya.detail_attribute.usia.min || usia > datanya.detail_attribute.usia.max-1){
|
||||
toast.warning("Usia harus diantara "+datanya.detail_attribute.usia.min+" - "+`${datanya.detail_attribute.usia.max-1}`)
|
||||
//focus back to input id=usia
|
||||
usiaInputRef.current.focus();
|
||||
}else if(berat < datanya.detail_attribute.berat.min || berat > datanya.detail_attribute.berat.max-50){
|
||||
toast.warning("Berat harus diantara "+datanya.detail_attribute.berat.min+" - "+`${datanya.detail_attribute.berat.max-50}`)
|
||||
//focus back to input id=berat
|
||||
beratInputRef.current.focus();
|
||||
}else if(keliling < datanya.detail_attribute.keliling.min || keliling > datanya.detail_attribute.keliling.max-1){
|
||||
toast.warning("Keliling harus diantara "+datanya.detail_attribute.keliling.min+" - "+`${datanya.detail_attribute.keliling.max-1}`)
|
||||
//focus back to input id=keliling
|
||||
kelilingInputRef.current.focus();
|
||||
}else if(ukuran_batang < datanya.detail_attribute.ukuran_batang.min || ukuran_batang > datanya.detail_attribute.ukuran_batang.max-0.5){
|
||||
toast.warning("Ukuran batang harus diantara "+datanya.detail_attribute.ukuran_batang.min+" - "+`${datanya.detail_attribute.ukuran_batang.max-0.5}`)
|
||||
//focus back to input id=ukuran_batang
|
||||
ukuranBatangInputRef.current.focus();
|
||||
}else if(jarak_duri < datanya.detail_attribute.jarak_duri.min || jarak_duri > datanya.detail_attribute.jarak_duri.max-0.5){
|
||||
toast.warning("Jarak duri harus diantara "+datanya.detail_attribute.jarak_duri.min+" - "+`${datanya.detail_attribute.jarak_duri.max-0.5}`)
|
||||
//focus back to input id=jarak_duri
|
||||
jarakDuriInputRef.current.focus();
|
||||
}
|
||||
else{
|
||||
const datanya = {
|
||||
usia: usia,
|
||||
berat: berat,
|
||||
keliling: keliling,
|
||||
ukuran_batang: ukuran_batang,
|
||||
jarak_duri: jarak_duri,
|
||||
};
|
||||
|
||||
// console.log(datanya);
|
||||
let formdata = new FormData();
|
||||
formdata.append("data", JSON.stringify(datanya));
|
||||
setLoading(true);
|
||||
try {
|
||||
//fetch api post
|
||||
const response = await fetch("http://127.0.0.1:5000/", {
|
||||
method: "POST",
|
||||
body: formdata,
|
||||
})
|
||||
const responseData = await response.json();
|
||||
|
||||
if(response.status === 200){
|
||||
console.log(responseData);
|
||||
await MySwal.fire({
|
||||
title: `<strong>${responseData.ket}</strong>`,
|
||||
html: <i>Hasil yang diinput adalah <b>{responseData.ket}</b> </i>,
|
||||
icon: 'success',
|
||||
showConfirmButton: false,
|
||||
})
|
||||
|
||||
}else{
|
||||
// create swal with 2 button ok and load , if ok is click then console.log("ok") else if load is click then console.log("load")
|
||||
await MySwal.fire({
|
||||
title: <strong>Gagal</strong>,
|
||||
html: <i>Server Bermasalah </i>,
|
||||
icon: 'error',
|
||||
showConfirmButton: true,
|
||||
showDenyButton: true,
|
||||
confirmButtonText: 'Reload Page',
|
||||
}).then(async (result) => {
|
||||
if (result.value) {
|
||||
window.location.reload();
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
setLoading(false);
|
||||
await MySwal.fire({
|
||||
title: <strong>Gagal</strong>,
|
||||
html: <i>Server Bermasalah </i>,
|
||||
icon: 'error',
|
||||
showConfirmButton: true,
|
||||
showDenyButton: true,
|
||||
confirmButtonText: 'Reload Page',
|
||||
}).then(async (result) => {
|
||||
if (result.value) {
|
||||
window.location.reload();
|
||||
}
|
||||
})
|
||||
}
|
||||
// props.onAddMeetup(meetupData)
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// create function oninput only accept number and .
|
||||
function onInput(event) {
|
||||
const value = event.target.value;
|
||||
const regex = /^[0-9.]+$/;
|
||||
if (!regex.test(value)) {
|
||||
event.target.value = value.slice(0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<Card>
|
||||
<ToastContainer draggable={false} transition={Zoom} autoClose={2000} />
|
||||
<Backdrop open={loading}><CircularProgress color="inherit" /></Backdrop>
|
||||
<form className={classess.form} onSubmit={submitHandler}>
|
||||
<div className={classess.control}>
|
||||
<label htmlFor="title">Usia <i>(Minggu)</i> </label>
|
||||
<input type="text" required id="usia" ref={usiaInputRef} placeholder={(datanya.status == 'error') ? 'Gagal Loading Server...' : `min ${datanya.detail_attribute.usia.min} - ${datanya.detail_attribute.usia.max-1} max` } onInput={onInput} minLength="1" maxLength="4" />
|
||||
</div>
|
||||
<div className={classess.control}>
|
||||
<label htmlFor="image">Berat <i>(Gram)</i> </label>
|
||||
<input type="text" required id="berat" placeholder={(datanya.status == 'error') ? 'Gagal Loading Server...' : `min ${datanya.detail_attribute.berat.min} - ${datanya.detail_attribute.berat.max-50} max` } ref={beratInputRef} onInput={onInput} minLength="3" maxLength="5" />
|
||||
</div>
|
||||
<div className={classess.control}>
|
||||
<label htmlFor="address">Keliling Buah <i>(cm)</i> </label>
|
||||
<input type="text" required id="keliling" placeholder={(datanya.status == 'error') ? 'Gagal Loading Server...' : `min ${datanya.detail_attribute.keliling.min} - ${datanya.detail_attribute.keliling.max-1} max` } ref={kelilingInputRef} onInput={onInput} minLength="2" maxLength="4"/>
|
||||
</div>
|
||||
<div className={classess.control}>
|
||||
<label htmlFor="description">Ukuran Batang <i>(cm)</i></label>
|
||||
<input type="text" required id="ukuran_batang" placeholder={(datanya.status == 'error') ? 'Gagal Loading Server...' : `min ${datanya.detail_attribute.ukuran_batang.min} - ${datanya.detail_attribute.ukuran_batang.max-0.5} max` } ref={ukuranBatangInputRef} onInput={onInput} minLength="1" maxLength="3"/>
|
||||
</div>
|
||||
<div className={classess.control}>
|
||||
<label htmlFor="description">Jarak Duri <i>(mm)</i></label>
|
||||
<input type="text" required id="jarak_duri" placeholder={(datanya.status == 'error') ? 'Gagal Loading Server...' : `min ${datanya.detail_attribute.jarak_duri.min} - ${datanya.detail_attribute.jarak_duri.max-0.5} max` } ref={jarakDuriInputRef} onInput={onInput} minLength="1" maxLength="4"/>
|
||||
</div>
|
||||
<div className={classess.actions} >
|
||||
<button>Cek Kematangan</button>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default InputData;
|
||||
87
frontend/components/halaman/InputData.module.css
Normal file
@ -0,0 +1,87 @@
|
||||
.form {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.control {
|
||||
margin-bottom: 0.5rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.control label {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.control input,
|
||||
.control textarea {
|
||||
display: block;
|
||||
font: inherit;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.25rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.actions button {
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
background-color: #77002e;
|
||||
color: white;
|
||||
padding: 0.5rem 1.5rem;
|
||||
border: 1px solid #77002e;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.actions button:hover,
|
||||
.actions button:active {
|
||||
background-color: #a50e48;
|
||||
border-color: #a50e48;
|
||||
}
|
||||
|
||||
.table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
.table1 {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table th, .table td {
|
||||
border: 0px solid #ccc;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.table1 th, .table1 td {
|
||||
border: none;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
|
||||
.table tr:nth-child(even) {
|
||||
background-color: #d3d3d3;
|
||||
}
|
||||
|
||||
.table tr:hover {
|
||||
background-color: #eaeaea;
|
||||
}
|
||||
|
||||
/* style the .table header */
|
||||
.table th {
|
||||
background-color: #77002e;
|
||||
color: white;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
46
frontend/components/halaman/Penulis.js
Normal file
@ -0,0 +1,46 @@
|
||||
import classess from "./InputData.module.css";
|
||||
import Card from "../ui/Card";
|
||||
function Penulis(){
|
||||
return(
|
||||
<Card>
|
||||
<form className={classess.form} >
|
||||
<div className={classess.control}>
|
||||
<table className={classess.table1}>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td width="25%"> Nama </td>
|
||||
<td> <i>:</i> </td>
|
||||
<td> <b> Namanya</b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td> NIM </td>
|
||||
<td> <i>:</i> </td>
|
||||
<td> <b> Namanya</b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td> Pembimbing 1 </td>
|
||||
<td> <i>:</i> </td>
|
||||
<td> <b> Namanya</b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td> Pembimbing 2 </td>
|
||||
<td> <i>:</i> </td>
|
||||
<td> <b> Namanya</b> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td> Judul </td>
|
||||
<td> <i>:</i> </td>
|
||||
<td> <b> Namanya</b> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default Penulis;
|
||||
13
frontend/components/layout/Layout.js
Normal file
@ -0,0 +1,13 @@
|
||||
import classes from "./Layout.module.css";
|
||||
import MainNavigation from "./MainNavigation";
|
||||
|
||||
function Layout(props) {
|
||||
return (
|
||||
<div>
|
||||
<MainNavigation />
|
||||
<main className={classes.main}>{props.children}</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
5
frontend/components/layout/Layout.module.css
Normal file
@ -0,0 +1,5 @@
|
||||
.main {
|
||||
margin: 3rem auto;
|
||||
width: 90%;
|
||||
max-width: 40rem;
|
||||
}
|
||||
20
frontend/components/layout/MainNavigation.js
Normal file
@ -0,0 +1,20 @@
|
||||
import Link from "next/link";
|
||||
|
||||
import classess from './MainNavigation.module.css'
|
||||
|
||||
function MainNavigation() {
|
||||
return (
|
||||
<header className={classess.header}>
|
||||
<div className={classess.logo}>Fuzzy Durian</div>
|
||||
<nav className={classess.nav}>
|
||||
<ul>
|
||||
<li><Link href='/'>Cek Data</Link></li>
|
||||
<li><Link href='/dataset'>Dataset</Link></li>
|
||||
<li><Link href='/penulis'>Penulis</Link></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export default MainNavigation;
|
||||
61
frontend/components/layout/MainNavigation.module.css
Normal file
@ -0,0 +1,61 @@
|
||||
.header {
|
||||
width: 100%;
|
||||
height: 5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: #77002e;
|
||||
padding: 0 10%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 2rem;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.header ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.header li {
|
||||
margin-left: 3rem;
|
||||
}
|
||||
|
||||
.header a {
|
||||
text-decoration: none;
|
||||
font-size: 1.5rem;
|
||||
color: #fcb8d2;
|
||||
}
|
||||
|
||||
.header a:hover,
|
||||
.header a:active,
|
||||
.header a.active {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge {
|
||||
background-color: #cc2062;
|
||||
color: white;
|
||||
border-radius: 12px;
|
||||
padding: 0 1rem;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
/* if display < 600px then hide logo */
|
||||
@media screen and (max-width: 600px) {
|
||||
.header .logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* set the li from left to right */
|
||||
.header li {
|
||||
margin-left: 0;
|
||||
/* add space between li */
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
9
frontend/components/ui/Card.js
Normal file
@ -0,0 +1,9 @@
|
||||
import classes from "./Card.module.css";
|
||||
|
||||
function Card(props) {
|
||||
return <div className={classes.card}>
|
||||
{props.children}
|
||||
</div>
|
||||
}
|
||||
|
||||
export default Card;
|
||||
5
frontend/components/ui/Card.module.css
Normal file
@ -0,0 +1,5 @@
|
||||
.card {
|
||||
background-color: white;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
6
frontend/next.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
6761
frontend/package-lock.json
generated
Normal file
27
frontend/package.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.8.2",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@mui/material": "^5.5.3",
|
||||
"next": "12.1.4",
|
||||
"nextjs-progressbar": "^0.0.14",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-toastify": "^8.2.0",
|
||||
"sweetalert2": "^11.4.8",
|
||||
"sweetalert2-react-content": "^4.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "8.12.0",
|
||||
"eslint-config-next": "12.1.4"
|
||||
}
|
||||
}
|
||||
15
frontend/pages/_app.js
Normal file
@ -0,0 +1,15 @@
|
||||
import Layout from '../components/layout/Layout'
|
||||
import '../styles/globals.css'
|
||||
import NextNProgress from "nextjs-progressbar";
|
||||
|
||||
|
||||
function MyApp({ Component, pageProps }) {
|
||||
return (
|
||||
<>
|
||||
<NextNProgress />
|
||||
<Layout><Component {...pageProps} /></Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyApp
|
||||
41
frontend/pages/dataset/index.js
Normal file
@ -0,0 +1,41 @@
|
||||
import DataSet from "../../components/halaman/DataSet";
|
||||
import Card from "../../components/ui/Card";
|
||||
|
||||
function PageDataSet(props){
|
||||
let data = JSON.parse(props.data);
|
||||
// console.log(data)
|
||||
let status = JSON.parse(props.data).status;
|
||||
// console.log(status)
|
||||
|
||||
return (status == "success") ?<DataSet data={data} /> : <Card> <br /> <center> <h2>Server Bermasalah ...</h2></center> <br /></Card>
|
||||
|
||||
// return <h1>okok</h1>
|
||||
}
|
||||
|
||||
export async function getStaticProps() { // this code never end up on client side ,excecuted during build time
|
||||
// fetch data from an API, for example
|
||||
let data;
|
||||
try {
|
||||
//fetches data http://127.0.0.1:5000/ with method get
|
||||
const response = await fetch('http://127.0.0.1:5000/', {
|
||||
method: 'GET',
|
||||
})
|
||||
//convert response to json
|
||||
data = await response.json()
|
||||
data['status'] = 'success'
|
||||
|
||||
} catch (e) {
|
||||
data = {"error": e,'status':'error'}
|
||||
}
|
||||
|
||||
// console.log(data)
|
||||
|
||||
return {
|
||||
props: { //this has to be named `props`
|
||||
data: JSON.stringify(data)
|
||||
},
|
||||
revalidate: 10 //revalidate after 10 second
|
||||
}
|
||||
}
|
||||
|
||||
export default PageDataSet;
|
||||
34
frontend/pages/index.js
Normal file
@ -0,0 +1,34 @@
|
||||
import InputData from "../components/halaman/InputData";
|
||||
|
||||
function HomePage(props) {
|
||||
const data = JSON.parse(props.data);
|
||||
return <InputData data={data} />;
|
||||
}
|
||||
|
||||
export async function getStaticProps() { // this code never end up on client side ,excecuted during build time
|
||||
// fetch data from an API, for example
|
||||
let data;
|
||||
try {
|
||||
//fetches data http://127.0.0.1:5000/ with method get
|
||||
const response = await fetch('http://127.0.0.1:5000/', {
|
||||
method: 'GET',
|
||||
})
|
||||
//convert response to json
|
||||
data = await response.json()
|
||||
data['status'] = 'success'
|
||||
|
||||
} catch (e) {
|
||||
data = {"error": e,'status':'error'}
|
||||
}
|
||||
|
||||
// console.log(data)
|
||||
|
||||
return {
|
||||
props: { //this has to be named `props`
|
||||
data: JSON.stringify(data)
|
||||
},
|
||||
revalidate: 10 //revalidate after 10 second
|
||||
}
|
||||
}
|
||||
|
||||
export default HomePage;
|
||||
7
frontend/pages/penulis.js
Normal file
@ -0,0 +1,7 @@
|
||||
import Penulis from "../components/halaman/Penulis";
|
||||
|
||||
function penulisPage(){
|
||||
return <Penulis />
|
||||
}
|
||||
|
||||
export default penulisPage;
|
||||
BIN
frontend/public/favicon.ico
Normal file
|
After Width: | Height: | Size: 25 KiB |
4
frontend/public/vercel.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
16
frontend/styles/globals.css
Normal file
@ -0,0 +1,16 @@
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||