first commit

This commit is contained in:
2024-12-27 16:46:43 +07:00
commit 1962bfb8cc
24 changed files with 7415 additions and 0 deletions

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example
+75
View File
@@ -0,0 +1,75 @@
# Nuxt Minimal Starter
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Setup
Make sure to install dependencies:
```bash
# npm
npm install
# pnpm
pnpm install
# yarn
yarn install
# bun
bun install
```
## Development Server
Start the development server on `http://localhost:3000`:
```bash
# npm
npm run dev
# pnpm
pnpm dev
# yarn
yarn dev
# bun
bun run dev
```
## Production
Build the application for production:
```bash
# npm
npm run build
# pnpm
pnpm build
# yarn
yarn build
# bun
bun run build
```
Locally preview production build:
```bash
# npm
npm run preview
# pnpm
pnpm preview
# yarn
yarn preview
# bun
bun run preview
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
+22
View File
@@ -0,0 +1,22 @@
<template>
<div>
<NuxtRouteAnnouncer />
<!-- <NuxtWelcome />-->
<!-- <v-btn @click="toggleTheme">toggle theme</v-btn>-->
<NuxtLayout>
<v-app>
<NuxtPage />
</v-app>
</NuxtLayout>
</div>
</template>
<script lang="ts" setup>
import { useTheme } from 'vuetify'
const theme = useTheme()
function toggleTheme () {
theme.global.name.value = theme.global.current.value.dark ? 'light' : 'dark'
}
</script>
+64
View File
@@ -0,0 +1,64 @@
<!-- components/form/login.vue -->
<template>
<div class="flex flex-col items-start justify-center w-full">
<CommonLogo class="h-[40px]" />
<h2 class="font-bold text-xl md:text-2xl my-4">
Login user
</h2>
<form
@submit.prevent="handleSubmitLogin"
class="flex gap-4 flex-wrap w-full">
<div class="w-full">
<label class="text-sm block">Email address:</label>
<InputText
@blur="validateEmail"
v-model="data.email.value"
:invalid="!!data.email.error"
size="small" class="w-full" type="email" name="email" placeholder="Email address" />
</div>
<div class="w-full">
<label class="text-sm block">Confirm Password:</label>
<InputText
@blur="validatePassword"
v-model="data.password.value"
:invalid="!!data.password.error"
size="small" class="w-full" type="password" name="password" placeholder="Password" />
</div>
<div class="error-message">
<p class="text-[12px] text-red-500" v-if="!!data.email.error">{{data.email.error}}</p>
<p class="text-[12px] text-red-500" v-if="!!data.password.error">{{data.password.error}}</p>
<p class="text-[12px] text-red-500" v-if="status === 'error'">
{{error}}
</p>
</div>
<Button
type="submit"
:loading="status === 'loading'"
:disabled="status === 'loading' || !isFormValid"
class="text-sm md:text-md w-full justify-center h-[48px]">Login</Button>
</form>
</div>
</template>
<script setup lang="ts">
import useLogin from "~/composables/auth/userAuth";
const { data , status, error, login , isFormValid, validatePassword, validateEmail } = useLogin()
async function handleSubmitLogin() {
await login()
if(status.value === 'success') {
navigateTo('/dashboard', { replace: true })
}
}
</script>
<style scoped lang="css">
</style>
View File
View File
+59
View File
@@ -0,0 +1,59 @@
// composables/auth/useLogin.ts
import {z, ZodError} from "zod";
import {computed} from "vue";
export default function useLogin() {
const data = reactive({
email: {
value: '',
error: ""
},
password: {
value: '',
error: ""
}
});
const status = ref<'idle' | 'loading' | 'success' | 'error'>('idle');
const error = ref<string | null>(null);
const validateEmail = () => {
const schema = z.string().email("Invalid email address");
try {
schema.parse(data.email.value);
data.email.error = "";
} catch (e: unknown) {
data.email.error = (e as ZodError).issues[0].message;
}
};
const validatePassword = () => {
const schema = z.string({message: "Password is required"})
.min(6, "Password must be at least 6 characters");
try {
schema.parse(data.password.value);
data.password.error = "";
} catch (e: unknown) {
data.password.error = (e as ZodError).issues[0].message;
}
};
const login = async () => {
console.log("Will Login");
}
const isFormValid = computed(() => {
return (
data.email.value !== "" &&
data.password.value !== "" &&
data.email.error === "" &&
data.password.error === ""
);
});
return {
data, login, status, error, validateEmail, validatePassword, isFormValid
}
}
+29
View File
@@ -0,0 +1,29 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
export default defineNuxtConfig({
build: {
transpile: ['vuetify'],
},
modules: [
(_options, nuxt) => {
nuxt.hooks.hook('vite:extendConfig', (config) => {
// @ts-expect-error
config.plugins.push(vuetify({ autoImport: true }))
})
},
"@pinia/nuxt",
// 'nuxt-zod-i18n',
// '@nuxtjs/i18n'
],
vite: {
vue: {
template: {
transformAssetUrls,
},
},
},
compatibilityDate: '2024-11-01',
devtools: { enabled: true }
})
+32
View File
@@ -0,0 +1,32 @@
{
"name": "pendaftaran",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"@nuxt/icon": "1.10.3",
"@pinia/nuxt": "^0.5.5",
"axios": "^1.7.7",
"nuxt": "^3.14.1592",
"nuxt-zod-i18n": "^1.11.2",
"pinia": "^2.2.4",
"sweetalert2": "^11.14.5",
"vue": "latest",
"vue-router": "latest",
"zod": "^3.24.1"
},
"devDependencies": {
"@mdi/font": "^7.4.47",
"@nuxtjs/google-fonts": "^3.0.0-1",
"@nuxtjs/tailwindcss": "^6.12.2",
"sass-embedded": "^1.80.5",
"vite-plugin-vuetify": "^2.0.4",
"vuetify": "^3.7.6"
}
}
+34
View File
@@ -0,0 +1,34 @@
<template>
<div>
halaman utama
</div>
<div>
<!-- <p>{{ message }}</p>-->
<p>{{ token }}</p><br>
<p>{{ user }}</p>
</div>
</template>
<script lang="ts" setup>
import {defineComponent,ref} from 'vue';
import {useCookie} from "#app";
import {useAuthentication} from "~/store/login";
import {storeToRefs} from "pinia";
const {userAuth} = useAuthentication()
const {user} = storeToRefs(useAuthentication())
const token = ref('')
token.value = useCookie('token');
// token.value = user;
const param = {
'token': useCookie('token'),
}
onMounted(()=>{
// await userAuth(useCookie('token'))
userAuth(param)
})
</script>
+13
View File
@@ -0,0 +1,13 @@
<template>
<!-- <div class="page">-->
<!-- well play-->
<!-- </div>-->
<!-- <Login />-->
</template>
sdaasd
<script setup lang="ts">
// import Login from "~/components/login.vue";
</script>
+124
View File
@@ -0,0 +1,124 @@
<template>
<div>
<v-img
class="mx-auto my-6"
max-width="228"
src="https://cdn.vuetifyjs.com/docs/images/logos/vuetify-logo-v3-slim-text-light.svg"
></v-img>
<v-card
class="mx-auto pa-12 pb-8"
elevation="8"
max-width="448"
rounded="lg"
>
<!-- <div class="text-subtitle-1 text-medium-emphasis">Account</div>-->
<v-text-field
density="compact"
placeholder="Email address"
prepend-inner-icon="mdi-email-outline"
variant="outlined"
></v-text-field>
<!-- <div class="text-subtitle-1 text-medium-emphasis d-flex align-center justify-space-between">-->
<!-- Password-->
<!-- <a-->
<!-- class="text-caption text-decoration-none text-blue"-->
<!-- href="#"-->
<!-- rel="noopener noreferrer"-->
<!-- target="_blank"-->
<!-- >-->
<!-- Forgot login password?</a>-->
<!-- </div>-->
<v-text-field
:append-inner-icon="visible ? 'mdi-eye-off' : 'mdi-eye'"
:type="visible ? 'text' : 'password'"
density="compact"
placeholder="Enter your password"
prepend-inner-icon="mdi-lock-outline"
variant="outlined"
@click:append-inner="visible = !visible"
></v-text-field>
<v-card
class="mb-6"
color="surface-variant"
variant="tonal"
>
<!-- <v-card-text class="text-medium-emphasis text-caption">-->
<!-- Warning: After 3 consecutive failed login attempts, you account will be temporarily locked for three hours. If you must login now, you can also click "Forgot login password?" below to reset the login password.-->
<!-- </v-card-text>-->
</v-card>
<v-btn
class="mb-8"
color="blue"
size="large"
variant="tonal"
block
@click="login"
>
Log In
</v-btn>
<p>{{token}}</p>
<p>{{ resAuth.data }}</p>
<!-- <v-card-text class="text-center">-->
<!-- <a-->
<!-- class="text-blue text-decoration-none"-->
<!-- href="#"-->
<!-- rel="noopener noreferrer"-->
<!-- target="_blank"-->
<!-- >-->
<!-- Sign up now <v-icon icon="mdi-chevron-right"></v-icon>-->
<!-- </a>-->
<!-- </v-card-text>-->
</v-card>
</div>
</template>
<script lang="ts" setup>
import {ref} from 'vue';
import {fetch} from "ofetch";
import {useRouter} from "#vue-router";
import {storeToRefs} from "pinia";
import {useCookie} from "#app";
import {useAuthentication} from "~/store/login";
const {auth} = useAuthentication()
const {resAuth} = storeToRefs(useAuthentication())
const visible = ref(false);
const router = useRouter()
const email = ref('');
const password = ref('');
const token = ref('')
const body = {
'email': '[email protected]',
'password': '123',
}
const login = async () => {
try {
await auth(body);
} catch (error) {
console.log(error);
}
token.value = useCookie('token');
navigateTo({
path: '/homePage',
// query: {
// token: useCookie('token')
// }
});
}
</script>
+121
View File
@@ -0,0 +1,121 @@
<template>
<div>
<v-img
class="mx-auto my-6"
max-width="228"
src="https://cdn.vuetifyjs.com/docs/images/logos/vuetify-logo-v3-slim-text-light.svg"
></v-img>
<v-card
class="mx-auto pa-12 pb-8"
elevation="8"
max-width="448"
rounded="lg"
>
<v-text-field
v-model="user_name"
density="compact"
placeholder="User Name"
prepend-inner-icon="mdi-account-key-outline"
variant="outlined"
></v-text-field>
<v-text-field
v-model="name"
density="compact"
placeholder="User Name"
prepend-inner-icon="mdi-account-circle-outline"
variant="outlined"
></v-text-field>
<v-text-field
v-model="email"
density="compact"
placeholder="Email address"
prepend-inner-icon="mdi-email-outline"
variant="outlined"
></v-text-field>
<v-text-field
v-model="password"
density="compact"
placeholder="Enter your password"
prepend-inner-icon="mdi-lock-outline"
variant="outlined"
:append-inner-icon="visible ? 'mdi-eye-off' : 'mdi-eye'"
:type="visible ? 'text' : 'password'"
@click:append-inner="visible = !visible"
></v-text-field>
<!-- <v-text-field-->
<!-- v-model:value="password"-->
<!-- :append-inner-icon="visible ? 'mdi-eye-off' : 'mdi-eye'"-->
<!-- :type="visible ? 'text' : 'password'"-->
<!-- density="compact"-->
<!-- placeholder="Enter your password"-->
<!-- prepend-inner-icon="mdi-lock-outline"-->
<!-- variant="outlined"-->
<!-- @click:append-inner="visible = !visible"-->
<!-- ></v-text-field>-->
<v-card
class="mb-6"
color="surface-variant"
variant="tonal"
>
</v-card>
<v-btn
class="mb-8"
color="blue"
size="large"
variant="tonal"
block
@click="register"
>
Register
</v-btn>
</v-card>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import {fetch} from "ofetch";
import {useRouter} from "#vue-router";
const router=useRouter();
const visible = ref(false);
const user_name = ref('');
const name = ref('');
const email = ref('');
const password = ref('');
const register = async () => {
console.log(user_name.value);
console.log(name.value);
console.log(email.value);
console.log(password.value);
const response=await fetch('http://127.0.0.1:8000/api/register', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
"name": name.value,
"user_name": user_name.value,
"email": email.value,
// "password": "123"
"password": password.value
})
});
console.log(response.status)
if (response.status != 200) {
console.log('gagal');
}else{
await router.push('/login');
}
}
</script>
+57
View File
@@ -0,0 +1,57 @@
// import this after install `@mdi/font` package
import '@mdi/font/css/materialdesignicons.css'
import 'vuetify/styles'
import { createApp } from 'vue'
import { createVuetify } from 'vuetify'
const customTheme = {
dark: false,
colors: {
background: '#FFFFFF',
surface: '#FFFFFF',
'surface-bright': '#FFFFFF',
'surface-light': '#EEEEEE',
'surface-variant': '#424242',
'on-surface-variant': '#EEEEEE',
primary: '#1867C0',
'primary-darken-1': '#1F5592',
secondary: '#48A9A6',
'secondary-darken-1': '#018786',
error: '#B00020',
info: '#2196F3',
success: '#4CAF50',
warning: '#FB8C00',
},
variables: {
'border-color': '#000000',
'border-opacity': 0.12,
'high-emphasis-opacity': 0.87,
'medium-emphasis-opacity': 0.60,
'disabled-opacity': 0.38,
'idle-opacity': 0.04,
'hover-opacity': 0.04,
'focus-opacity': 0.12,
'selected-opacity': 0.08,
'activated-opacity': 0.12,
'pressed-opacity': 0.12,
'dragged-opacity': 0.08,
'theme-kbd': '#212529',
'theme-on-kbd': '#FFFFFF',
'theme-code': '#F5F5F5',
'theme-on-code': '#000000',
}
}
export default defineNuxtPlugin((app) => {
const vuetify = createVuetify({
// ... your configuration
theme: {
defaultTheme: 'customTheme',
themes: {
customTheme,
},
},
})
app.vueApp.use(vuetify)
})
+6586
View File
File diff suppressed because it is too large Load diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+1
View File
@@ -0,0 +1 @@
+19
View File
@@ -0,0 +1,19 @@
import axios from "axios";
export default defineEventHandler(async (event) => {
const body = await readBody(event);
// const email = body.email
// const password = body.password
console.log(body.email)
console.log(body.password)
try {
const response = await axios.post("http://10.10.150.129:8082/api/login/" + body);
return response.data
} catch (error) {
console.error("Error posting to surat kontrol API:", error);
throw createError({
statusCode: 500,
statusMessage: "Failed to fetch data from surat kontrol API",
});
}
});
+3
View File
@@ -0,0 +1,3 @@
{
"extends": "../.nuxt/tsconfig.server.json"
}
+19
View File
@@ -0,0 +1,19 @@
import {H3Event} from "h3";
import Client from "~/utils/api/client";
export const ServerApi = (event: H3Event) => {
const {apiUrl} = useRuntimeConfig().public;
const accessToken = getCookie(event, 'Authorization');
const refreshToken = getCookie(event, 'Refresh-Token');
console.log(`Access Token: ${accessToken}`);
console.log(`Refresh Token: ${refreshToken}`);
const client = new Client(apiUrl as string, {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Refresh-Token': `Bearer ${refreshToken}`
} as HeadersInit
})
return client;
}
+55
View File
@@ -0,0 +1,55 @@
import {defineStore} from "pinia";
import {ref} from "vue";
import {useCookie} from "#app";
// ___________________AUTH_________________________
export const useAuthentication = defineStore("Authentication", () => {
// ________________AUTH____________________________
const resAuth = ref<any[]>([]);
const auth = async (body: Record<string, any>) => {
try {
resAuth.value = await $fetch("http://127.0.0.1:8000/api/login/", {
method: "POST",
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(body)
});
// resultAuth.value = resAuth._rawValue;
// console.log(resAuth._rawValue.token);
useCookie('token').value = resAuth._rawValue.token;
} catch (err) {
throw createError({
statusCode: 400,
statusMessage: "Failed to fetch data from Authentication API",
});
}
};
// __________________USER AUTH_______________________________________
const user = ref<any[]>([]);
const userAuth = async (body: Record<string, any>) => {
console.log(body.token)
try {
user.value = await $fetch("http://127.0.0.1:8000/api/userAuth/", {
method: "GET",
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${useCookie('token').value}`,
'X-Requested-With': 'XMLHttpRequest',
},
})
console.log(user.value)
console.log(user)
} catch (err) {
navigateTo({
path: '/login',
});
throw createError({
statusCode: 400,
statusMessage: "Failed to fetch data from User Auth API",
});
}
}
return {auth, resAuth, userAuth, user}
})
+4
View File
@@ -0,0 +1,4 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}
+74
View File
@@ -0,0 +1,74 @@
import type {FetchOptions} from 'ofetch';
export default class Client {
options?: FetchOptions;
baseUrl: string;
constructor(baseUrl: string, options?: FetchOptions) {
this.options = options;
this.baseUrl = baseUrl;
}
async raw<T>(url: string, method: 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | 'get' | 'head' | 'patch' | 'post' | 'put' | 'delete' | 'connect' | 'options' | 'trace') {
try {
const response = await $fetch.raw<T>(`${this.baseUrl}${url}`, {
// ...options,
...this.options,
method
})
return response;
} catch (err) {
return Promise.reject(err);
}
}
async post<T>(ur: string, options?: FetchOptions,) {
try {
const response = await $fetch.raw<T>(`${this.baseUrl}${ur}`, {
...options,
...this.options,
method: 'POST'
})
return response;
} catch (err) {
return Promise.reject(err);
}
}
async get<T>(ur: string, options?: FetchOptions,) {
try {
const response = await $fetch.raw<T>(`${this.baseUrl}${ur}`, {
...options,
...this.options,
method: 'GET'
})
return response;
} catch (err) {
return Promise.reject(err);
}
}
async put<T>(ur: string, options?: FetchOptions,) {
try {
const response = await $fetch.raw<T>(`${this.baseUrl}${ur}`, {
...options,
...this.options,
method: 'PUT'
})
return response;
} catch (err) {
return Promise.reject(err);
}
}
async del<T>(ur: string, options?: FetchOptions,) {
try {
const response = await $fetch.raw<T>(`${this.baseUrl}${ur}`, {
...options,
...this.options,
method: 'DELETE'
})
return response;
} catch (err) {
return Promise.reject(err);
}
}
}