generated from Flycro/laravel-nuxt
Initial commit
This commit is contained in:
27
nuxt/pages/account.vue
Normal file
27
nuxt/pages/account.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script lang="ts" setup>
|
||||
definePageMeta({
|
||||
middleware: ["auth"],
|
||||
});
|
||||
|
||||
const links = [
|
||||
[
|
||||
{
|
||||
label: "Account",
|
||||
icon: "i-heroicons-user",
|
||||
to: "/account/general",
|
||||
},
|
||||
{
|
||||
label: "Devices",
|
||||
icon: "i-heroicons-device-phone-mobile",
|
||||
to: "/account/devices",
|
||||
},
|
||||
],
|
||||
];
|
||||
</script>
|
||||
<template>
|
||||
<UHorizontalNavigation
|
||||
:links="links"
|
||||
class="border-b border-gray-200 dark:border-gray-800 mb-4"
|
||||
/>
|
||||
<NuxtPage class="col-span-10 md:col-span-8" />
|
||||
</template>
|
||||
86
nuxt/pages/account/devices.vue
Normal file
86
nuxt/pages/account/devices.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<script lang="ts" setup>
|
||||
const dayjs = useDayjs();
|
||||
const auth = useAuthStore();
|
||||
const loading = ref(false);
|
||||
const devices = ref([]);
|
||||
|
||||
async function fetchData() {
|
||||
loading.value = true;
|
||||
|
||||
const response = await $fetch<any>("devices");
|
||||
|
||||
if (response.ok) {
|
||||
devices.value = response.devices;
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: "name",
|
||||
label: "Device",
|
||||
},
|
||||
{
|
||||
key: "last_used_at",
|
||||
label: "Last used at",
|
||||
class: "max-w-[9rem] w-[9rem] min-w-[9rem]",
|
||||
},
|
||||
{
|
||||
key: "actions",
|
||||
},
|
||||
];
|
||||
|
||||
const items = (row: any) => [
|
||||
[
|
||||
{
|
||||
label: "Delete",
|
||||
icon: "i-heroicons-trash-20-solid",
|
||||
click: async () => {
|
||||
await $fetch<any>("devices/disconnect", {
|
||||
method: "POST",
|
||||
body: {
|
||||
hash: row.hash,
|
||||
},
|
||||
async onResponse({ response }) {
|
||||
if (response._data?.ok) {
|
||||
await fetchData();
|
||||
await auth.fetchUser();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
if (process.client) {
|
||||
fetchData();
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<UCard :ui="{ body: { padding: 'p-0' } }">
|
||||
<ClientOnly>
|
||||
<UTable :rows="devices" :columns="columns" size="lg" :loading="loading">
|
||||
<template #name-data="{ row }">
|
||||
<div class="font-semibold">
|
||||
{{ row.name }}
|
||||
<UBadge v-if="row.is_current" label="active" color="emerald" variant="soft" size="xs" class="ms-1" />
|
||||
</div>
|
||||
<div class="font-medium text-sm">IP: {{ row.ip }}</div>
|
||||
</template>
|
||||
<template #last_used_at-data="{ row }">
|
||||
{{ dayjs(row.last_used_at).fromNow() }}
|
||||
</template>
|
||||
<template #actions-data="{ row }">
|
||||
<div class="flex justify-end">
|
||||
<UDropdown :items="items(row)">
|
||||
<UButton :disabled="row.is_current" color="gray" variant="ghost"
|
||||
icon="i-heroicons-ellipsis-horizontal-20-solid" />
|
||||
</UDropdown>
|
||||
</div>
|
||||
</template>
|
||||
</UTable>
|
||||
</ClientOnly>
|
||||
</UCard>
|
||||
</template>
|
||||
24
nuxt/pages/account/general.vue
Normal file
24
nuxt/pages/account/general.vue
Normal file
@@ -0,0 +1,24 @@
|
||||
<script lang="ts" setup></script>
|
||||
<template>
|
||||
<UCard :ui="{ body: { base: 'grid grid-cols-12 gap-6 md:gap-8' } }">
|
||||
<div class="col-span-12 lg:col-span-4">
|
||||
<div class="text-lg font-semibold mb-2">Profile information</div>
|
||||
<div class="text-sm opacity-80">
|
||||
Update your account's profile information and email address.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-12 lg:col-span-8">
|
||||
<AccountUpdateProfile />
|
||||
</div>
|
||||
<UDivider class="col-span-12" />
|
||||
<div class="col-span-12 lg:col-span-4">
|
||||
<div class="text-lg font-semibold mb-2">Update Password</div>
|
||||
<div class="text-sm opacity-80">
|
||||
Ensure your account is using a long, random password to stay secure.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-span-12 lg:col-span-8">
|
||||
<AccountUpdatePassword />
|
||||
</div>
|
||||
</UCard>
|
||||
</template>
|
||||
6
nuxt/pages/account/index.vue
Normal file
6
nuxt/pages/account/index.vue
Normal file
@@ -0,0 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
definePageMeta({
|
||||
redirect: "/account/general",
|
||||
});
|
||||
</script>
|
||||
<template></template>
|
||||
62
nuxt/pages/forgot-password/index.vue
Normal file
62
nuxt/pages/forgot-password/index.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ middleware: ['guest'], layout: 'auth' })
|
||||
|
||||
const form = ref();
|
||||
|
||||
const state = reactive({
|
||||
email: "",
|
||||
});
|
||||
|
||||
const { refresh: onSubmit, status: forgotStatus } = useFetch<any>("forgot-password", {
|
||||
method: "POST",
|
||||
body: state,
|
||||
immediate: false,
|
||||
watch: false,
|
||||
async onResponse({ response }) {
|
||||
if (response?.status === 422) {
|
||||
form.value.setErrors(response._data?.errors);
|
||||
} else if (response._data?.ok) {
|
||||
useToast().add({
|
||||
title: "Success",
|
||||
description: response._data.message,
|
||||
color: "emerald",
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UMain>
|
||||
<UPage>
|
||||
<div class="mx-auto flex min-h-screen w-full items-center justify-center">
|
||||
<UCard class="w-96">
|
||||
<template #header>
|
||||
<div class="space-y-4 text-center ">
|
||||
<h1 class="text-2xl font-bold">
|
||||
Forgot Password
|
||||
</h1>
|
||||
<p class="text-sm">
|
||||
Remember your password? <NuxtLink to="/login" class="text-primary hover:text-primary-300 font-semibold">
|
||||
Login here
|
||||
</NuxtLink>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<UForm ref="form" :state="state" class="space-y-8" @submit="onSubmit">
|
||||
<UFormGroup label="Email" name="email">
|
||||
<UInput v-model="state.email" />
|
||||
</UFormGroup>
|
||||
|
||||
<UButton block size="md" type="submit" :loading="forgotStatus === 'pending'" icon="i-heroicons-envelope">
|
||||
Reset Password
|
||||
</UButton>
|
||||
</UForm>
|
||||
|
||||
<!-- <UDivider label="OR" class=" my-4"/> -->
|
||||
</UCard>
|
||||
</div>
|
||||
</UPage>
|
||||
</UMain>
|
||||
</template>
|
||||
13
nuxt/pages/index.vue
Normal file
13
nuxt/pages/index.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ middleware: ['auth'] })
|
||||
|
||||
const modal = useModal();
|
||||
const router = useRouter();
|
||||
const auth = useAuthStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
This is the Page Content
|
||||
</div>
|
||||
</template>
|
||||
103
nuxt/pages/login/index.vue
Normal file
103
nuxt/pages/login/index.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ middleware: ['guest'], layout: 'auth' })
|
||||
|
||||
const config = useRuntimeConfig();
|
||||
const router = useRouter();
|
||||
const auth = useAuthStore();
|
||||
const form = ref();
|
||||
|
||||
type Provider = {
|
||||
name: string;
|
||||
icon: string;
|
||||
color: string;
|
||||
loading?: boolean;
|
||||
};
|
||||
|
||||
const state = reactive({
|
||||
email: "",
|
||||
password: "",
|
||||
remember: false,
|
||||
});
|
||||
|
||||
const { refresh: onSubmit, status: loginStatus } = useFetch<any>("login", {
|
||||
method: "POST",
|
||||
body: state,
|
||||
immediate: false,
|
||||
watch: false,
|
||||
async onResponse({ response }) {
|
||||
if (response?.status === 422) {
|
||||
form.value.setErrors(response._data?.errors);
|
||||
} else if (response._data?.ok) {
|
||||
auth.token = response._data.token;
|
||||
|
||||
await auth.fetchUser();
|
||||
await router.push("/");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const providers = ref<{ [key: string]: Provider }>(config.public.providers);
|
||||
|
||||
async function handleMessage(event: { data: any }): Promise<void> {
|
||||
const provider = event.data.provider as string;
|
||||
|
||||
if (Object.keys(providers.value).includes(provider) && event.data.token) {
|
||||
providers.value[provider].loading = false;
|
||||
auth.token = event.data.token;
|
||||
|
||||
await auth.fetchUser();
|
||||
await router.push("/");
|
||||
} else if (event.data.message) {
|
||||
useToast().add({
|
||||
icon: "i-heroicons-exclamation-circle-solid",
|
||||
color: "red",
|
||||
title: event.data.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UMain>
|
||||
<UPage>
|
||||
<div class="mx-auto flex min-h-screen w-full items-center justify-center">
|
||||
<UCard class="w-96">
|
||||
<template #header>
|
||||
<h1 class="text-center text-2xl font-bold">
|
||||
Login
|
||||
</h1>
|
||||
</template>
|
||||
|
||||
<UForm ref="form" :state="state" class="space-y-4" @submit="onSubmit">
|
||||
<UFormGroup label="Email" name="email">
|
||||
<UInput v-model="state.email" />
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Password" name="password">
|
||||
<UInput v-model="state.password" type="password" />
|
||||
</UFormGroup>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<UCheckbox id="remember-me" v-model="state.remember" label="Remember me" name="remember-me" />
|
||||
|
||||
<div class="text-sm leading-6">
|
||||
<NuxtLink to="/forgot-password" class="text-primary hover:text-primary-300 font-semibold">
|
||||
Forgot
|
||||
password?
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UButton block size="md" type="submit" :loading="loginStatus === 'pending'" icon="i-heroicons-arrow-right-on-rectangle">
|
||||
Login
|
||||
</UButton>
|
||||
</UForm>
|
||||
|
||||
<!-- <UDivider label="OR" class=" my-4"/> -->
|
||||
</UCard>
|
||||
</div>
|
||||
</UPage>
|
||||
</UMain>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
10
nuxt/pages/logout/index.vue
Normal file
10
nuxt/pages/logout/index.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ middleware: ['auth'], layout: 'auth' });
|
||||
const auth = useAuthStore()
|
||||
auth.logout()
|
||||
</script>
|
||||
78
nuxt/pages/password-reset/[token].vue
Normal file
78
nuxt/pages/password-reset/[token].vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({ middleware: ['guest'], layout: 'auth' })
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const auth = useAuthStore();
|
||||
const form = ref();
|
||||
|
||||
const state = reactive({
|
||||
email: route.query.email as string,
|
||||
token: route.params.token,
|
||||
password: "",
|
||||
password_confirmation: "",
|
||||
});
|
||||
|
||||
const { refresh: onSubmit, status: resetStatus } = useFetch<any>("reset-password", {
|
||||
method: "POST",
|
||||
body: state,
|
||||
immediate: false,
|
||||
watch: false,
|
||||
async onResponse({ response }) {
|
||||
if (response?.status === 422) {
|
||||
form.value.setErrors(response._data?.errors);
|
||||
} else if (response._data?.ok) {
|
||||
useToast().add({
|
||||
title: "Success",
|
||||
description: response._data.message,
|
||||
color: "emerald",
|
||||
});
|
||||
|
||||
if (auth.isLoggedIn) {
|
||||
await auth.fetchUser();
|
||||
await router.push("/");
|
||||
} else {
|
||||
await router.push("/login");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UMain>
|
||||
<UPage>
|
||||
<div class="mx-auto flex min-h-screen w-full items-center justify-center">
|
||||
<UCard class="w-96">
|
||||
<template #header>
|
||||
<h1 class="text-center text-2xl font-bold">
|
||||
Reset Password
|
||||
</h1>
|
||||
</template>
|
||||
|
||||
<UForm ref="form" :state="state" class="space-y-4" @submit="onSubmit">
|
||||
<UFormGroup label="Email" name="email">
|
||||
<UInput v-model="state.email" disabled />
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Password" name="password">
|
||||
<UInput v-model="state.password" type="password" />
|
||||
</UFormGroup>
|
||||
|
||||
<UFormGroup label="Confirm Password" name="password_confirmation">
|
||||
<UInput v-model="state.password_confirmation" type="password" />
|
||||
</UFormGroup>
|
||||
|
||||
<UButton block size="md" type="submit" :loading="resetStatus === 'pending'" icon="i-heroicon-lock-closed">
|
||||
Change Password
|
||||
</UButton>
|
||||
</UForm>
|
||||
|
||||
<!-- <UDivider label="OR" class=" my-4"/> -->
|
||||
</UCard>
|
||||
</div>
|
||||
</UPage>
|
||||
</UMain>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user