fahrulputraa40 commited on
Commit
8e0fdc4
·
verified ·
1 Parent(s): d2972dd

Upload 8 files

Browse files
frontend/index.html CHANGED
@@ -4,7 +4,7 @@
4
  <meta charset="UTF-8">
5
  <link rel="icon" href="/favicon.ico">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Vite App</title>
8
  </head>
9
  <body>
10
  <div id="app"></div>
 
4
  <meta charset="UTF-8">
5
  <link rel="icon" href="/favicon.ico">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Cifar 10 Class Prediction</title>
8
  </head>
9
  <body>
10
  <div id="app"></div>
frontend/src/App.vue CHANGED
@@ -1,85 +1,7 @@
1
  <script setup lang="ts">
2
  import { RouterLink, RouterView } from 'vue-router'
3
- import HelloWorld from './components/HelloWorld.vue'
4
  </script>
5
 
6
  <template>
7
- <header>
8
- <img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
9
-
10
- <div class="wrapper">
11
- <HelloWorld msg="You did it!" />
12
-
13
- <nav>
14
- <RouterLink to="/">Home</RouterLink>
15
- <RouterLink to="/about">About</RouterLink>
16
- </nav>
17
- </div>
18
- </header>
19
-
20
  <RouterView />
21
- </template>
22
-
23
- <style scoped>
24
- header {
25
- line-height: 1.5;
26
- max-height: 100vh;
27
- }
28
-
29
- .logo {
30
- display: block;
31
- margin: 0 auto 2rem;
32
- }
33
-
34
- nav {
35
- width: 100%;
36
- font-size: 12px;
37
- text-align: center;
38
- margin-top: 2rem;
39
- }
40
-
41
- nav a.router-link-exact-active {
42
- color: var(--color-text);
43
- }
44
-
45
- nav a.router-link-exact-active:hover {
46
- background-color: transparent;
47
- }
48
-
49
- nav a {
50
- display: inline-block;
51
- padding: 0 1rem;
52
- border-left: 1px solid var(--color-border);
53
- }
54
-
55
- nav a:first-of-type {
56
- border: 0;
57
- }
58
-
59
- @media (min-width: 1024px) {
60
- header {
61
- display: flex;
62
- place-items: center;
63
- padding-right: calc(var(--section-gap) / 2);
64
- }
65
-
66
- .logo {
67
- margin: 0 2rem 0 0;
68
- }
69
-
70
- header .wrapper {
71
- display: flex;
72
- place-items: flex-start;
73
- flex-wrap: wrap;
74
- }
75
-
76
- nav {
77
- text-align: left;
78
- margin-left: -1rem;
79
- font-size: 1rem;
80
-
81
- padding: 1rem 0;
82
- margin-top: 1rem;
83
- }
84
- }
85
- </style>
 
1
  <script setup lang="ts">
2
  import { RouterLink, RouterView } from 'vue-router'
 
3
  </script>
4
 
5
  <template>
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  <RouterView />
7
+ </template>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/assets/base.css CHANGED
@@ -1,86 +1,5 @@
1
- /* color palette from <https://github.com/vuejs/theme> */
2
- :root {
3
- --vt-c-white: #ffffff;
4
- --vt-c-white-soft: #f8f8f8;
5
- --vt-c-white-mute: #f2f2f2;
6
-
7
- --vt-c-black: #181818;
8
- --vt-c-black-soft: #222222;
9
- --vt-c-black-mute: #282828;
10
-
11
- --vt-c-indigo: #2c3e50;
12
-
13
- --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
14
- --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
15
- --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
16
- --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
17
-
18
- --vt-c-text-light-1: var(--vt-c-indigo);
19
- --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
20
- --vt-c-text-dark-1: var(--vt-c-white);
21
- --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
22
- }
23
-
24
- /* semantic color variables for this project */
25
- :root {
26
- --color-background: var(--vt-c-white);
27
- --color-background-soft: var(--vt-c-white-soft);
28
- --color-background-mute: var(--vt-c-white-mute);
29
-
30
- --color-border: var(--vt-c-divider-light-2);
31
- --color-border-hover: var(--vt-c-divider-light-1);
32
-
33
- --color-heading: var(--vt-c-text-light-1);
34
- --color-text: var(--vt-c-text-light-1);
35
-
36
- --section-gap: 160px;
37
- }
38
-
39
- @media (prefers-color-scheme: dark) {
40
- :root {
41
- --color-background: var(--vt-c-black);
42
- --color-background-soft: var(--vt-c-black-soft);
43
- --color-background-mute: var(--vt-c-black-mute);
44
-
45
- --color-border: var(--vt-c-divider-dark-2);
46
- --color-border-hover: var(--vt-c-divider-dark-1);
47
-
48
- --color-heading: var(--vt-c-text-dark-1);
49
- --color-text: var(--vt-c-text-dark-2);
50
- }
51
- }
52
-
53
- *,
54
- *::before,
55
- *::after {
56
  box-sizing: border-box;
57
- margin: 0;
58
- font-weight: normal;
59
- }
60
-
61
- body {
62
- min-height: 100vh;
63
- color: var(--color-text);
64
- background: var(--color-background);
65
- transition:
66
- color 0.5s,
67
- background-color 0.5s;
68
- line-height: 1.6;
69
- font-family:
70
- Inter,
71
- -apple-system,
72
- BlinkMacSystemFont,
73
- 'Segoe UI',
74
- Roboto,
75
- Oxygen,
76
- Ubuntu,
77
- Cantarell,
78
- 'Fira Sans',
79
- 'Droid Sans',
80
- 'Helvetica Neue',
81
- sans-serif;
82
- font-size: 15px;
83
- text-rendering: optimizeLegibility;
84
- -webkit-font-smoothing: antialiased;
85
- -moz-osx-font-smoothing: grayscale;
86
- }
 
1
+ *{
2
+ margin: 0;
3
+ padding: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  box-sizing: border-box;
5
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/main.ts CHANGED
@@ -1,4 +1,4 @@
1
- import './assets/main.css'
2
 
3
  import { createApp } from 'vue'
4
  import { createPinia } from 'pinia'
 
1
+ import './assets/base.css'
2
 
3
  import { createApp } from 'vue'
4
  import { createPinia } from 'pinia'
frontend/src/router/index.ts CHANGED
@@ -9,14 +9,6 @@ const router = createRouter({
9
  name: 'home',
10
  component: HomeView,
11
  },
12
- {
13
- path: '/about',
14
- name: 'about',
15
- // route level code-splitting
16
- // this generates a separate chunk (About.[hash].js) for this route
17
- // which is lazy-loaded when the route is visited.
18
- component: () => import('../views/AboutView.vue'),
19
- },
20
  ],
21
  })
22
 
 
9
  name: 'home',
10
  component: HomeView,
11
  },
 
 
 
 
 
 
 
 
12
  ],
13
  })
14
 
frontend/src/views/HomeView.vue CHANGED
@@ -1,9 +1,187 @@
1
- <script setup lang="ts">
2
- import TheWelcome from '../components/TheWelcome.vue'
3
- </script>
4
-
5
  <template>
6
- <main>
7
- <TheWelcome />
8
- </main>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  </template>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <template>
2
+ <div class="container">
3
+ <h1>CIFAR-10 Image Checker</h1>
4
+
5
+ <label class="upload-el">
6
+ <input type="file" accept="image/*" @change="fileChanged" />
7
+ <span>Select Image</span>
8
+ </label>
9
+
10
+ <div v-if="prev" class="preview">
11
+ <img :src="prev" alt="Preview" />
12
+ </div>
13
+
14
+ <button v-if="prev" class="submit" :disabled="!imageFile || loading" @click="submitImage">
15
+ {{ loading ? 'Loading...' : 'Check Class' }}
16
+ </button>
17
+
18
+ <div v-if="loading" class="loader"></div>
19
+
20
+ <div v-if="prediction" class="result">
21
+ <h3>Hasil Prediksi:</h3>
22
+ <p>{{ prediction }}</p>
23
+ </div>
24
+ </div>
25
  </template>
26
+
27
+ <script>
28
+ export default {
29
+ data() {
30
+ return {
31
+ imageFile: null,
32
+ prev: null,
33
+ prediction: null,
34
+ loading: false,
35
+ }
36
+ },
37
+ methods: {
38
+ fileChanged(event) {
39
+ const file = event.target.files[0]
40
+ if (file) {
41
+ this.imageFile = file
42
+ this.prev = URL.createObjectURL(file)
43
+ this.prediction = null
44
+ }
45
+ },
46
+ async submitImage() {
47
+ if(this.loading == true) return;
48
+ this.loading = true
49
+ this.prediction = null
50
+
51
+ const formData = new FormData()
52
+ formData.append('file', this.imageFile)
53
+
54
+ try {
55
+ const response = await fetch('/predict', {
56
+ method: 'POST',
57
+ body: formData,
58
+ })
59
+ const data = await response.json();
60
+ if(data.error){
61
+ this.prediction = data.error;
62
+ return;
63
+ }
64
+
65
+ const classNames = [
66
+ 'Pesawat',
67
+ 'Mobil',
68
+ 'Burung',
69
+ 'Kucing',
70
+ 'Rusa',
71
+ 'Anjing',
72
+ 'Katak',
73
+ 'Kuda',
74
+ 'Kambing',
75
+ 'Truk',
76
+ ]
77
+
78
+ const index = data.class
79
+ this.prediction = `${classNames[index]} (Confident Level: ${(data.confidence * 100).toFixed(2)}%)`
80
+ } catch (err) {
81
+ console.error(err)
82
+ this.prediction = 'Maaf, terjadi kesalahan ketika proses prediksi';
83
+ } finally {
84
+ this.loading = false
85
+ }
86
+ },
87
+ },
88
+ }
89
+ </script>
90
+
91
+ <style scoped>
92
+ .container {
93
+ max-width: 480px;
94
+ margin: 40px auto;
95
+ padding: 24px;
96
+ border-radius: 12px;
97
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.08);
98
+ background-color: #fff;
99
+ font-family: 'Segoe UI', sans-serif;
100
+ text-align: center;
101
+ }
102
+
103
+ h1 {
104
+ margin-bottom: 24px;
105
+ color: #7a7a7a;
106
+ font-size: 24px;
107
+ }
108
+
109
+ .upload-el {
110
+ display: block;
111
+ background-color: #e0e7ff;
112
+ color: #1e40af;
113
+ padding: 10px 20px;
114
+ border-radius: 6px;
115
+ font-weight: 600;
116
+ cursor: pointer;
117
+ margin-bottom: 16px;
118
+ transition: background 0.3s ease;
119
+ }
120
+
121
+ .upload-el:hover {
122
+ background-color: #c7d2fe;
123
+ }
124
+
125
+ .upload-el input {
126
+ display: none;
127
+ }
128
+
129
+ .preview img {
130
+ margin-top: 16px;
131
+ max-width: 100%;
132
+ min-width: 150px;
133
+ border-radius: 6px;
134
+ border: 1px solid #ddd;
135
+ }
136
+
137
+ .submit {
138
+ margin-top: 16px;
139
+ background-color: #2563eb;
140
+ color: white;
141
+ padding: 10px 20px;
142
+ border: none;
143
+ border-radius: 6px;
144
+ font-weight: bold;
145
+ cursor: pointer;
146
+ display: block;
147
+ width: 100%;
148
+ transition: background 0.3s ease;
149
+ }
150
+
151
+ .submit:disabled {
152
+ background-color: #9ca3af;
153
+ cursor: not-allowed;
154
+ }
155
+
156
+ .submit:hover:enabled {
157
+ background-color: #1d4ed8;
158
+ }
159
+
160
+ .result {
161
+ margin-top: 24px;
162
+ padding: 16px;
163
+ background-color: #f1f5f9;
164
+ border-left: 5px solid #2563eb;
165
+ border-radius: 6px;
166
+ text-align: left;
167
+ }
168
+
169
+ .loader {
170
+ margin: 20px auto;
171
+ border: 4px solid #e0e0e0;
172
+ border-top: 4px solid #2563eb;
173
+ border-radius: 50%;
174
+ width: 30px;
175
+ height: 30px;
176
+ animation: spin 1s linear infinite;
177
+ }
178
+
179
+ @keyframes spin {
180
+ 0% {
181
+ transform: rotate(0);
182
+ }
183
+ 100% {
184
+ transform: rotate(360deg);
185
+ }
186
+ }
187
+ </style>