This commit is contained in:
2024-03-17 12:30:19 +01:00
commit c805b5ebaa
133 changed files with 24036 additions and 0 deletions

27
nuxt/pages/account.vue Normal file
View 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>

View 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>

View 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>

View File

@@ -0,0 +1,6 @@
<script lang="ts" setup>
definePageMeta({
redirect: "/account/general",
});
</script>
<template></template>

View 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
View 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
View 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>

View File

@@ -0,0 +1,10 @@
<template>
<div>
</div>
</template>
<script setup lang="ts">
definePageMeta({ middleware: ['auth'], layout: 'auth' });
const auth = useAuthStore()
auth.logout()
</script>

View 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>