Initial commit
Some checks failed
Build / build (push) Has been cancelled

This commit is contained in:
koh
2025-04-28 13:51:59 +07:00
committed by GitHub
commit 7f02e92304
113 changed files with 11808 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
<script setup>
import { useForm, Head } from '@inertiajs/vue3'
import { ref } from 'vue'
import LayoutGuest from '@/layouts/LayoutGuest.vue'
import SectionFullScreen from '@/components/SectionFullScreen.vue'
import CardBox from '@/components/CardBox.vue'
import FormControl from '@/components/FormControl.vue'
import FormField from '@/components/FormField.vue'
import BaseDivider from '@/components/BaseDivider.vue'
import BaseButton from '@/components/BaseButton.vue'
import FormValidationErrors from '@/components/FormValidationErrors.vue'
const form = useForm({
password: ''
})
const passwordInput = ref(null)
const submit = () => {
form.post(route('password.confirm'), {
onFinish: () => {
form.reset()
passwordInput.value?.focus()
}
})
}
</script>
<template>
<LayoutGuest>
<Head title="Secure Area" />
<SectionFullScreen
v-slot="{ cardClass }"
bg="purplePink"
>
<CardBox
:class="cardClass"
is-form
@submit.prevent="submit"
>
<FormValidationErrors />
<FormField>
<div class="mb-4 text-sm text-gray-600">
This is a secure area of the application. Please confirm your password before continuing.
</div>
</FormField>
<FormField
label="Password"
label-for="password"
help="Please enter your password to confirm"
>
<FormControl
id="password"
@set-ref="passwordInput = $event"
v-model="form.password"
type="password"
required
autocomplete="current-password"
/>
</FormField>
<BaseDivider />
<BaseButton
type="submit"
color="info"
label="Confirm"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@@ -0,0 +1,91 @@
<script setup>
import { useForm, Head, Link } from '@inertiajs/vue3'
import { mdiEmail } from '@mdi/js'
import LayoutGuest from '@/layouts/LayoutGuest.vue'
import SectionFullScreen from '@/components/SectionFullScreen.vue'
import CardBox from '@/components/CardBox.vue'
import FormField from '@/components/FormField.vue'
import FormControl from '@/components/FormControl.vue'
import BaseDivider from '@/components/BaseDivider.vue'
import BaseButton from '@/components/BaseButton.vue'
import FormValidationErrors from '@/components/FormValidationErrors.vue'
import NotificationBarInCard from '@/components/NotificationBarInCard.vue'
import BaseLevel from '@/components/BaseLevel.vue'
defineProps({
status: {
type: String,
default: null
}
})
const form = useForm({
email: ''
})
const submit = () => {
form.post(route('password.email'))
}
</script>
<template>
<LayoutGuest>
<Head title="Forgot Password" />
<SectionFullScreen
v-slot="{ cardClass }"
bg="purplePink"
>
<CardBox
:class="cardClass"
is-form
@submit.prevent="submit"
>
<FormValidationErrors />
<NotificationBarInCard
v-if="status"
color="info"
>
{{ status }}
</NotificationBarInCard>
<FormField>
<div class="mb-4 text-sm text-gray-600">
Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.
</div>
</FormField>
<FormField
label="Email"
help="Please enter your email"
>
<FormControl
v-model="form.email"
:icon="mdiEmail"
autocomplete="email"
type="email"
required
/>
</FormField>
<BaseDivider />
<BaseLevel>
<BaseButton
type="submit"
color="info"
label="Email link"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
<Link
:href="route('login')"
>
Back to login
</Link>
</BaseLevel>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@@ -0,0 +1,129 @@
<script setup>
import { useForm, Head, Link } from '@inertiajs/vue3'
import { mdiAccount, mdiAsterisk } from '@mdi/js'
import LayoutGuest from '@/layouts/LayoutGuest.vue'
import SectionFullScreen from '@/components/SectionFullScreen.vue'
import CardBox from '@/components/CardBox.vue'
import FormCheckRadioGroup from '@/components/FormCheckRadioGroup.vue'
import FormField from '@/components/FormField.vue'
import FormControl from '@/components/FormControl.vue'
import BaseDivider from '@/components/BaseDivider.vue'
import BaseButton from '@/components/BaseButton.vue'
import BaseButtons from '@/components/BaseButtons.vue'
import FormValidationErrors from '@/components/FormValidationErrors.vue'
import NotificationBarInCard from '@/components/NotificationBarInCard.vue'
import BaseLevel from '@/components/BaseLevel.vue'
const props = defineProps({
canResetPassword: Boolean,
status: {
type: String,
default: null
}
})
const form = useForm({
email: '',
password: '',
remember: []
})
const submit = () => {
form
.transform(data => ({
... data,
remember: form.remember && form.remember.length ? 'on' : ''
}))
.post(route('login'), {
onFinish: () => form.reset('password'),
})
}
</script>
<template>
<LayoutGuest>
<Head title="Login" />
<SectionFullScreen
v-slot="{ cardClass }"
bg="purplePink"
>
<CardBox
:class="cardClass"
is-form
@submit.prevent="submit"
>
<FormValidationErrors />
<NotificationBarInCard
v-if="status"
color="info"
>
{{ status }}
</NotificationBarInCard>
<FormField
label="Email"
label-for="email"
help="Please enter your email"
>
<FormControl
v-model="form.email"
:icon="mdiAccount"
id="email"
autocomplete="email"
type="email"
required
/>
</FormField>
<FormField
label="Password"
label-for="password"
help="Please enter your password"
>
<FormControl
v-model="form.password"
:icon="mdiAsterisk"
type="password"
id="password"
autocomplete="current-password"
required
/>
</FormField>
<FormCheckRadioGroup
v-model="form.remember"
name="remember"
:options="{ remember: 'Remember' }"
/>
<BaseDivider />
<BaseLevel>
<BaseButtons>
<BaseButton
type="submit"
color="info"
label="Login"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
<BaseButton
v-if="canResetPassword"
route-name="password.request"
color="info"
outline
label="Remind"
/>
</BaseButtons>
<Link
:href="route('register')"
>
Register
</Link>
</BaseLevel>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@@ -0,0 +1,131 @@
<script setup>
import { useForm, usePage, Head } from "@inertiajs/vue3";
import { computed } from "vue";
import { mdiAccount, mdiEmail, mdiFormTextboxPassword } from "@mdi/js";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
import SectionFullScreen from "@/components/SectionFullScreen.vue";
import CardBox from "@/components/CardBox.vue";
import FormCheckRadioGroup from "@/components/FormCheckRadioGroup.vue";
import FormField from "@/components/FormField.vue";
import FormControl from "@/components/FormControl.vue";
import BaseDivider from "@/components/BaseDivider.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseButtons from "@/components/BaseButtons.vue";
import FormValidationErrors from "@/components/FormValidationErrors.vue";
const form = useForm({
name: "",
email: "",
password: "",
password_confirmation: "",
terms: [],
});
const hasTermsAndPrivacyPolicyFeature = computed(
() => usePage().props.jetstream?.hasTermsAndPrivacyPolicyFeature
);
const submit = () => {
form
.transform((data) => ({
...data,
terms: form.terms && form.terms.length,
}))
.post(route("register"), {
onFinish: () => form.reset("password", "password_confirmation"),
});
};
</script>
<template>
<LayoutGuest>
<Head title="Register" />
<SectionFullScreen v-slot="{ cardClass }" bg="purplePink">
<CardBox
:class="cardClass"
class="my-24"
is-form
@submit.prevent="submit"
>
<FormValidationErrors />
<FormField label="Name" label-for="name" help="Please enter your name">
<FormControl
v-model="form.name"
id="name"
:icon="mdiAccount"
autocomplete="name"
type="text"
required
/>
</FormField>
<FormField
label="Email"
label-for="email"
help="Please enter your email"
>
<FormControl
v-model="form.email"
id="email"
:icon="mdiEmail"
autocomplete="email"
type="email"
required
/>
</FormField>
<FormField
label="Password"
label-for="password"
help="Please enter new password"
>
<FormControl
v-model="form.password"
id="password"
:icon="mdiFormTextboxPassword"
type="password"
autocomplete="new-password"
required
/>
</FormField>
<FormField
label="Confirm Password"
label-for="password_confirmation"
help="Please confirm your password"
>
<FormControl
v-model="form.password_confirmation"
id="password_confirmation"
:icon="mdiFormTextboxPassword"
type="password"
autocomplete="new-password"
required
/>
</FormField>
<FormCheckRadioGroup
v-if="hasTermsAndPrivacyPolicyFeature"
v-model="form.terms"
name="remember"
:options="{ agree: 'I agree to the Terms' }"
/>
<BaseDivider />
<BaseButtons>
<BaseButton
type="submit"
color="info"
label="Register"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
<BaseButton route-name="login" color="info" outline label="Login" />
</BaseButtons>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@@ -0,0 +1,111 @@
<script setup>
import { useForm, Head, Link } from '@inertiajs/vue3'
import { mdiEmail, mdiFormTextboxPassword } from '@mdi/js'
import LayoutGuest from '@/layouts/LayoutGuest.vue'
import SectionFullScreen from '@/components/SectionFullScreen.vue'
import CardBox from '@/components/CardBox.vue'
import FormField from '@/components/FormField.vue'
import FormControl from '@/components/FormControl.vue'
import BaseDivider from '@/components/BaseDivider.vue'
import BaseButton from '@/components/BaseButton.vue'
import FormValidationErrors from '@/components/FormValidationErrors.vue'
const props = defineProps({
email: {
type: String,
default: null
},
token: {
type: String,
default: null
}
})
const form = useForm({
token: props.token,
email: props.email,
password: '',
password_confirmation: '',
})
const submit = () => {
form
.post(route('password.update'), {
onFinish: () => form.reset('password', 'password_confirmation'),
})
}
</script>
<template>
<LayoutGuest>
<Head title="Reset Password" />
<SectionFullScreen
v-slot="{ cardClass }"
bg="purplePink"
>
<CardBox
:class="cardClass"
is-form
@submit.prevent="submit"
>
<FormValidationErrors />
<FormField
label="Email"
label-for="email"
help="Please enter your email"
>
<FormControl
v-model="form.email"
:icon="mdiEmail"
autocomplete="email"
type="email"
id="email"
required
/>
</FormField>
<FormField
label="Password"
label-for="password"
help="Please enter new password"
>
<FormControl
v-model="form.password"
:icon="mdiFormTextboxPassword"
type="password"
autocomplete="new-password"
id="password"
required
/>
</FormField>
<FormField
label="Confirm Password"
label-for="password_confirmation"
help="Please confirm new password"
>
<FormControl
v-model="form.password_confirmation"
:icon="mdiFormTextboxPassword"
type="password"
autocomplete="new-password"
id="password_confirmation"
required
/>
</FormField>
<BaseDivider />
<BaseButton
type="submit"
color="info"
label="Reset password"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@@ -0,0 +1,126 @@
<script setup>
import { useForm, Head } from '@inertiajs/vue3'
import { nextTick, ref } from 'vue'
import LayoutGuest from '@/layouts/LayoutGuest.vue'
import SectionFullScreen from '@/components/SectionFullScreen.vue'
import CardBox from '@/components/CardBox.vue'
import FormControl from '@/components/FormControl.vue'
import FormField from '@/components/FormField.vue'
import BaseDivider from '@/components/BaseDivider.vue'
import BaseButton from '@/components/BaseButton.vue'
import FormValidationErrors from '@/components/FormValidationErrors.vue'
import BaseLevel from '@/components/BaseLevel.vue'
const recovery = ref(false)
const form = useForm({
code: '',
recovery_code: ''
})
const recoveryCodeInput = ref(null)
const codeInput = ref(null)
const toggleRecovery = async () => {
recovery.value ^= true
await nextTick()
if (recovery.value) {
recoveryCodeInput.value?.focus()
form.code = ''
} else {
codeInput.value?.focus()
form.recovery_code = ''
}
}
const submit = () => {
form.post(route('two-factor.login'))
}
</script>
<template>
<LayoutGuest>
<Head title="Two-factor Confirmation" />
<SectionFullScreen
v-slot="{ cardClass }"
bg="purplePink"
>
<CardBox
:class="cardClass"
is-form
@submit.prevent="submit"
>
<FormValidationErrors />
<FormField>
<div class="mb-4 text-sm text-gray-600">
<template v-if="! recovery">
Please confirm access to your account by entering the authentication code provided by your authenticator application.
</template>
<template v-else>
Please confirm access to your account by entering one of your emergency recovery codes.
</template>
</div>
</FormField>
<FormField
v-if="!recovery"
label="Code"
label-for="code"
help="Please enter one-time code"
>
<FormControl
id="code"
@set-ref="codeInput = $event"
v-model="form.code"
type="text"
inputmode="numeric"
autofocus
autocomplete="one-time-code"
/>
</FormField>
<FormField
v-else
label="Recovery Code"
label-for="recovery_code"
help="Please enter recovery code"
>
<FormControl
id="recovery_code"
@set-ref="recoveryCodeInput = $event"
v-model="form.recovery_code"
type="text"
class="mt-1 block w-full"
autocomplete="one-time-code"
/>
</FormField>
<BaseDivider />
<BaseLevel>
<BaseButton
type="submit"
color="info"
label="Log in"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
<button @click.prevent="toggleRecovery">
<template v-if="!recovery">
Use a recovery code
</template>
<template v-else>
Use an authentication code
</template>
</button>
</BaseLevel>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@@ -0,0 +1,70 @@
<script setup>
import { useForm, Head, Link } from "@inertiajs/vue3";
import { computed } from "vue";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
import SectionFullScreen from "@/components/SectionFullScreen.vue";
import CardBox from "@/components/CardBox.vue";
import FormField from "@/components/FormField.vue";
import BaseDivider from "@/components/BaseDivider.vue";
import BaseButton from "@/components/BaseButton.vue";
import FormValidationErrors from "@/components/FormValidationErrors.vue";
import NotificationBarInCard from "@/components/NotificationBarInCard.vue";
import BaseLevel from "@/components/BaseLevel.vue";
const props = defineProps({
status: {
type: String,
default: null,
},
});
const form = useForm();
const verificationLinkSent = computed(
() => props.status === "verification-link-sent"
);
const submit = () => {
form.post(route("verification.send"));
};
</script>
<template>
<LayoutGuest>
<Head title="Email Verification" />
<SectionFullScreen v-slot="{ cardClass }" bg="purplePink">
<CardBox :class="cardClass" is-form @submit.prevent="submit">
<FormValidationErrors />
<NotificationBarInCard v-if="verificationLinkSent" color="info">
A new verification link has been sent to the email address you
provided during registration.
</NotificationBarInCard>
<FormField>
<div class="mb-4 text-sm text-gray-600">
Thanks for signing up! Before getting started, could you verify your
email address by clicking on the link we just emailed to you? If you
didn't receive the email, we will gladly send you another.
</div>
</FormField>
<BaseDivider />
<BaseLevel>
<BaseButton
type="submit"
color="info"
label="Resend Verification Email"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing"
/>
<Link :href="route('logout')" method="post" as="button">
Logout
</Link>
</BaseLevel>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>

View File

@@ -0,0 +1,37 @@
import '../css/main.css'
import { createPinia } from 'pinia'
// import { useDarkModeStore } from '@/stores/darkMode.js'
import { createApp, h } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m'
const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel'
const pinia = createPinia()
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) =>
resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
setup({ el, App, props, plugin }) {
return createApp({ render: () => h(App, props) })
.use(plugin)
.use(pinia)
.use(ZiggyVue, Ziggy)
.mount(el)
},
progress: {
color: '#4B5563'
}
})
// const darkModeStore = useDarkModeStore(pinia)
// if (
// (!localStorage['darkMode'] && window.matchMedia('(prefers-color-scheme: dark)').matches) ||
// localStorage['darkMode'] === '1'
// ) {
// darkModeStore.set(true)
// }

View File

@@ -0,0 +1,16 @@
<script setup>
import { computed } from "vue";
import { usePage } from "@inertiajs/vue3";
import NotificationBarInCard from "@/components/NotificationBarInCard.vue";
const errors = computed(() => usePage().props.errors);
const hasErrors = computed(() => Object.keys(errors.value).length > 0);
</script>
<template>
<NotificationBarInCard v-if="hasErrors" color="danger">
<b>Whoops! Something went wrong.</b>
<span v-for="(error, key) in errors" :key="key">{{ error }}</span>
</NotificationBarInCard>
</template>